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 <mach-o/fat.h>
35 #include <mach-o/arch.h>
36 #include <mach-o/loader.h>
38 // Some OS X SDKs don't define these.
40 #define CPU_TYPE_ARM ((cpu_type_t) 12)
42 #ifndef CPU_ARCH_ABI64
43 #define CPU_ARCH_ABI64 0x01000000 /* 64 bit ABI */
45 #ifndef CPU_TYPE_ARM64
46 #define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64)
49 // File abstraction taken from ld64/FileAbstraction.hpp
50 // and ld64/MachOFileAbstraction.hpp.
53 #define INLINE __attribute__((always_inline))
59 // This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants
61 // For example: to make a utility that handles 32-bit little enidan files use: Pointer32<LittleEndian>
64 // get16() read a 16-bit number from an E endian struct
65 // set16() write a 16-bit number to an E endian struct
66 // get32() read a 32-bit number from an E endian struct
67 // set32() write a 32-bit number to an E endian struct
68 // get64() read a 64-bit number from an E endian struct
69 // set64() write a 64-bit number to an E endian struct
71 // getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field)
72 // setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field)
74 // getBitsRaw() read a bit field from a struct with native endianness
75 // setBitsRaw() write a bit field from a struct with native endianness
81 static uint16_t get16(const uint16_t& from
) INLINE
{ return OSReadBigInt16(&from
, 0); }
82 static void set16(uint16_t& into
, uint16_t value
) INLINE
{ OSWriteBigInt16(&into
, 0, value
); }
84 static uint32_t get32(const uint32_t& from
) INLINE
{ return OSReadBigInt32(&from
, 0); }
85 static void set32(uint32_t& into
, uint32_t value
) INLINE
{ OSWriteBigInt32(&into
, 0, value
); }
87 static uint64_t get64(const uint64_t& from
) INLINE
{ return OSReadBigInt64(&from
, 0); }
88 static void set64(uint64_t& into
, uint64_t value
) INLINE
{ OSWriteBigInt64(&into
, 0, value
); }
90 static uint32_t getBits(const uint32_t& from
,
91 uint8_t firstBit
, uint8_t bitCount
) INLINE
{ return getBitsRaw(get32(from
), firstBit
, bitCount
); }
92 static void setBits(uint32_t& into
, uint32_t value
,
93 uint8_t firstBit
, uint8_t bitCount
) INLINE
{ uint32_t temp
= get32(into
); setBitsRaw(temp
, value
, firstBit
, bitCount
); set32(into
, temp
); }
95 static uint32_t getBitsRaw(const uint32_t& from
,
96 uint8_t firstBit
, uint8_t bitCount
) INLINE
{ return ((from
>> (32-firstBit
-bitCount
)) & ((1<<bitCount
)-1)); }
97 static void setBitsRaw(uint32_t& into
, uint32_t value
,
98 uint8_t firstBit
, uint8_t bitCount
) INLINE
{ uint32_t temp
= into
;
99 const uint32_t mask
= ((1<<bitCount
)-1);
100 temp
&= ~(mask
<< (32-firstBit
-bitCount
));
101 temp
|= ((value
& mask
) << (32-firstBit
-bitCount
));
103 enum { little_endian
= 0 };
110 static uint16_t get16(const uint16_t& from
) INLINE
{ return OSReadLittleInt16(&from
, 0); }
111 static void set16(uint16_t& into
, uint16_t value
) INLINE
{ OSWriteLittleInt16(&into
, 0, value
); }
113 static uint32_t get32(const uint32_t& from
) INLINE
{ return OSReadLittleInt32(&from
, 0); }
114 static void set32(uint32_t& into
, uint32_t value
) INLINE
{ OSWriteLittleInt32(&into
, 0, value
); }
116 static uint64_t get64(const uint64_t& from
) INLINE
{ return OSReadLittleInt64(&from
, 0); }
117 static void set64(uint64_t& into
, uint64_t value
) INLINE
{ OSWriteLittleInt64(&into
, 0, value
); }
119 static uint32_t getBits(const uint32_t& from
,
120 uint8_t firstBit
, uint8_t bitCount
) INLINE
{ return getBitsRaw(get32(from
), firstBit
, bitCount
); }
121 static void setBits(uint32_t& into
, uint32_t value
,
122 uint8_t firstBit
, uint8_t bitCount
) INLINE
{ uint32_t temp
= get32(into
); setBitsRaw(temp
, value
, firstBit
, bitCount
); set32(into
, temp
); }
124 static uint32_t getBitsRaw(const uint32_t& from
,
125 uint8_t firstBit
, uint8_t bitCount
) INLINE
{ return ((from
>> firstBit
) & ((1<<bitCount
)-1)); }
126 static void setBitsRaw(uint32_t& into
, uint32_t value
,
127 uint8_t firstBit
, uint8_t bitCount
) INLINE
{ uint32_t temp
= into
;
128 const uint32_t mask
= ((1<<bitCount
)-1);
129 temp
&= ~(mask
<< firstBit
);
130 temp
|= ((value
& mask
) << firstBit
);
132 enum { little_endian
= 1 };
136 typedef BigEndian CurrentEndian
;
137 typedef LittleEndian OtherEndian
;
138 #elif __LITTLE_ENDIAN__
139 typedef LittleEndian CurrentEndian
;
140 typedef BigEndian OtherEndian
;
142 #error unknown endianness
146 template <typename _E
>
150 typedef uint32_t uint_t
;
151 typedef int32_t sint_t
;
154 static uint64_t getP(const uint_t
& from
) INLINE
{ return _E::get32(from
); }
155 static void setP(uint_t
& into
, uint64_t value
) INLINE
{ _E::set32(into
, value
); }
159 template <typename _E
>
163 typedef uint64_t uint_t
;
164 typedef int64_t sint_t
;
167 static uint64_t getP(const uint_t
& from
) INLINE
{ return _E::get64(from
); }
168 static void setP(uint_t
& into
, uint64_t value
) INLINE
{ _E::set64(into
, value
); }
173 // mach-o file header
175 template <typename P
> struct macho_header_content
{};
176 template <> struct macho_header_content
<Pointer32
<BigEndian
> > { mach_header fields
; };
177 template <> struct macho_header_content
<Pointer64
<BigEndian
> > { mach_header_64 fields
; };
178 template <> struct macho_header_content
<Pointer32
<LittleEndian
> > { mach_header fields
; };
179 template <> struct macho_header_content
<Pointer64
<LittleEndian
> > { mach_header_64 fields
; };
181 template <typename P
>
184 uint32_t magic() const INLINE
{ return E::get32(header
.fields
.magic
); }
185 void set_magic(uint32_t value
) INLINE
{ E::set32(header
.fields
.magic
, value
); }
187 uint32_t cputype() const INLINE
{ return E::get32(header
.fields
.cputype
); }
188 void set_cputype(uint32_t value
) INLINE
{ E::set32((uint32_t&)header
.fields
.cputype
, value
); }
190 uint32_t cpusubtype() const INLINE
{ return E::get32(header
.fields
.cpusubtype
); }
191 void set_cpusubtype(uint32_t value
) INLINE
{ E::set32((uint32_t&)header
.fields
.cpusubtype
, value
); }
193 uint32_t filetype() const INLINE
{ return E::get32(header
.fields
.filetype
); }
194 void set_filetype(uint32_t value
) INLINE
{ E::set32(header
.fields
.filetype
, value
); }
196 uint32_t ncmds() const INLINE
{ return E::get32(header
.fields
.ncmds
); }
197 void set_ncmds(uint32_t value
) INLINE
{ E::set32(header
.fields
.ncmds
, value
); }
199 uint32_t sizeofcmds() const INLINE
{ return E::get32(header
.fields
.sizeofcmds
); }
200 void set_sizeofcmds(uint32_t value
) INLINE
{ E::set32(header
.fields
.sizeofcmds
, value
); }
202 uint32_t flags() const INLINE
{ return E::get32(header
.fields
.flags
); }
203 void set_flags(uint32_t value
) INLINE
{ E::set32(header
.fields
.flags
, value
); }
205 uint32_t reserved() const INLINE
{ return E::get32(header
.fields
.reserved
); }
206 void set_reserved(uint32_t value
) INLINE
{ E::set32(header
.fields
.reserved
, value
); }
208 typedef typename
P::E E
;
210 macho_header_content
<P
> header
;
215 // mach-o load command
217 template <typename P
>
218 class macho_load_command
{
220 uint32_t cmd() const INLINE
{ return E::get32(command
.cmd
); }
221 void set_cmd(uint32_t value
) INLINE
{ E::set32(command
.cmd
, value
); }
223 uint32_t cmdsize() const INLINE
{ return E::get32(command
.cmdsize
); }
224 void set_cmdsize(uint32_t value
) INLINE
{ E::set32(command
.cmdsize
, value
); }
226 typedef typename
P::E E
;
228 load_command command
;
235 // mach-o segment load command
237 template <typename P
> struct macho_segment_content
{};
238 template <> struct macho_segment_content
<Pointer32
<BigEndian
> > { segment_command fields
; enum { CMD
= LC_SEGMENT
}; };
239 template <> struct macho_segment_content
<Pointer64
<BigEndian
> > { segment_command_64 fields
; enum { CMD
= LC_SEGMENT_64
}; };
240 template <> struct macho_segment_content
<Pointer32
<LittleEndian
> > { segment_command fields
; enum { CMD
= LC_SEGMENT
}; };
241 template <> struct macho_segment_content
<Pointer64
<LittleEndian
> > { segment_command_64 fields
; enum { CMD
= LC_SEGMENT_64
}; };
243 template <typename P
>
244 class macho_segment_command
{
246 uint32_t cmd() const INLINE
{ return E::get32(segment
.fields
.cmd
); }
247 void set_cmd(uint32_t value
) INLINE
{ E::set32(segment
.fields
.cmd
, value
); }
249 uint32_t cmdsize() const INLINE
{ return E::get32(segment
.fields
.cmdsize
); }
250 void set_cmdsize(uint32_t value
) INLINE
{ E::set32(segment
.fields
.cmdsize
, value
); }
252 const char* segname() const INLINE
{ return segment
.fields
.segname
; }
253 void set_segname(const char* value
) INLINE
{ strncpy(segment
.fields
.segname
, value
, 16); }
255 uint64_t vmaddr() const INLINE
{ return P::getP(segment
.fields
.vmaddr
); }
256 void set_vmaddr(uint64_t value
) INLINE
{ P::setP(segment
.fields
.vmaddr
, value
); }
258 uint64_t vmsize() const INLINE
{ return P::getP(segment
.fields
.vmsize
); }
259 void set_vmsize(uint64_t value
) INLINE
{ P::setP(segment
.fields
.vmsize
, value
); }
261 uint64_t fileoff() const INLINE
{ return P::getP(segment
.fields
.fileoff
); }
262 void set_fileoff(uint64_t value
) INLINE
{ P::setP(segment
.fields
.fileoff
, value
); }
264 uint64_t filesize() const INLINE
{ return P::getP(segment
.fields
.filesize
); }
265 void set_filesize(uint64_t value
) INLINE
{ P::setP(segment
.fields
.filesize
, value
); }
267 uint32_t maxprot() const INLINE
{ return E::get32(segment
.fields
.maxprot
); }
268 void set_maxprot(uint32_t value
) INLINE
{ E::set32((uint32_t&)segment
.fields
.maxprot
, value
); }
270 uint32_t initprot() const INLINE
{ return E::get32(segment
.fields
.initprot
); }
271 void set_initprot(uint32_t value
) INLINE
{ E::set32((uint32_t&)segment
.fields
.initprot
, value
); }
273 uint32_t nsects() const INLINE
{ return E::get32(segment
.fields
.nsects
); }
274 void set_nsects(uint32_t value
) INLINE
{ E::set32(segment
.fields
.nsects
, value
); }
276 uint32_t flags() const INLINE
{ return E::get32(segment
.fields
.flags
); }
277 void set_flags(uint32_t value
) INLINE
{ E::set32(segment
.fields
.flags
, value
); }
280 CMD
= macho_segment_content
<P
>::CMD
283 typedef typename
P::E E
;
285 macho_segment_content
<P
> segment
;
292 template <typename P
> struct macho_section_content
{};
293 template <> struct macho_section_content
<Pointer32
<BigEndian
> > { section fields
; };
294 template <> struct macho_section_content
<Pointer64
<BigEndian
> > { section_64 fields
; };
295 template <> struct macho_section_content
<Pointer32
<LittleEndian
> > { section fields
; };
296 template <> struct macho_section_content
<Pointer64
<LittleEndian
> > { section_64 fields
; };
298 template <typename P
>
299 class macho_section
{
301 const char* sectname() const INLINE
{ return section
.fields
.sectname
; }
302 void set_sectname(const char* value
) INLINE
{ strncpy(section
.fields
.sectname
, value
, 16); }
304 const char* segname() const INLINE
{ return section
.fields
.segname
; }
305 void set_segname(const char* value
) INLINE
{ strncpy(section
.fields
.segname
, value
, 16); }
307 uint64_t addr() const INLINE
{ return P::getP(section
.fields
.addr
); }
308 void set_addr(uint64_t value
) INLINE
{ P::setP(section
.fields
.addr
, value
); }
310 uint64_t size() const INLINE
{ return P::getP(section
.fields
.size
); }
311 void set_size(uint64_t value
) INLINE
{ P::setP(section
.fields
.size
, value
); }
313 uint32_t offset() const INLINE
{ return E::get32(section
.fields
.offset
); }
314 void set_offset(uint32_t value
) INLINE
{ E::set32(section
.fields
.offset
, value
); }
316 uint32_t align() const INLINE
{ return E::get32(section
.fields
.align
); }
317 void set_align(uint32_t value
) INLINE
{ E::set32(section
.fields
.align
, value
); }
319 uint32_t reloff() const INLINE
{ return E::get32(section
.fields
.reloff
); }
320 void set_reloff(uint32_t value
) INLINE
{ E::set32(section
.fields
.reloff
, value
); }
322 uint32_t nreloc() const INLINE
{ return E::get32(section
.fields
.nreloc
); }
323 void set_nreloc(uint32_t value
) INLINE
{ E::set32(section
.fields
.nreloc
, value
); }
325 uint32_t flags() const INLINE
{ return E::get32(section
.fields
.flags
); }
326 void set_flags(uint32_t value
) INLINE
{ E::set32(section
.fields
.flags
, value
); }
328 uint32_t reserved1() const INLINE
{ return E::get32(section
.fields
.reserved1
); }
329 void set_reserved1(uint32_t value
) INLINE
{ E::set32(section
.fields
.reserved1
, value
); }
331 uint32_t reserved2() const INLINE
{ return E::get32(section
.fields
.reserved2
); }
332 void set_reserved2(uint32_t value
) INLINE
{ E::set32(section
.fields
.reserved2
, value
); }
334 typedef typename
P::E E
;
336 macho_section_content
<P
> section
;
342 static bool debug
= true;
344 bool processFile(const char *filename
);
346 int main(int argc
, const char *argv
[]) {
347 for (int i
= 1; i
< argc
; ++i
) {
348 if (!processFile(argv
[i
])) return 1;
359 // Segment and section names are 16 bytes and may be un-terminated.
360 bool segnameEquals(const char *lhs
, const char *rhs
)
362 return 0 == strncmp(lhs
, rhs
, 16);
365 bool segnameStartsWith(const char *segname
, const char *prefix
)
367 return 0 == strncmp(segname
, prefix
, strlen(prefix
));
370 bool sectnameEquals(const char *lhs
, const char *rhs
)
372 return segnameEquals(lhs
, rhs
);
376 template <typename P
>
377 void dosect(uint8_t *start
, macho_section
<P
> *sect
)
379 if (debug
) printf("section %.16s from segment %.16s\n",
380 sect
->sectname(), sect
->segname());
382 // Strip S_MOD_INIT/TERM_FUNC_POINTERS. We don't want dyld to call
383 // our init funcs because it is too late, and we don't want anyone to
384 // call our term funcs ever.
385 if (segnameStartsWith(sect
->segname(), "__DATA") &&
386 sectnameEquals(sect
->sectname(), "__mod_init_func"))
388 // section type 0 is S_REGULAR
389 sect
->set_flags(sect
->flags() & ~SECTION_TYPE
);
390 sect
->set_sectname("__objc_init_func");
391 if (debug
) printf("disabled __mod_init_func section\n");
393 if (segnameStartsWith(sect
->segname(), "__DATA") &&
394 sectnameEquals(sect
->sectname(), "__mod_term_func"))
396 // section type 0 is S_REGULAR
397 sect
->set_flags(sect
->flags() & ~SECTION_TYPE
);
398 sect
->set_sectname("__objc_term_func");
399 if (debug
) printf("disabled __mod_term_func section\n");
403 template <typename P
>
404 void doseg(uint8_t *start
, macho_segment_command
<P
> *seg
)
406 if (debug
) printf("segment name: %.16s, nsects %u\n",
407 seg
->segname(), seg
->nsects());
408 macho_section
<P
> *sect
= (macho_section
<P
> *)(seg
+ 1);
409 for (uint32_t i
= 0; i
< seg
->nsects(); ++i
) {
410 dosect(start
, §
[i
]);
416 bool parse_macho(uint8_t *buffer
)
418 macho_header
<P
>* mh
= (macho_header
<P
>*)buffer
;
419 uint8_t *cmds
= (uint8_t *)(mh
+ 1);
420 for (uint32_t c
= 0; c
< mh
->ncmds(); c
++) {
421 macho_load_command
<P
>* cmd
= (macho_load_command
<P
>*)cmds
;
422 cmds
+= cmd
->cmdsize();
423 if (cmd
->cmd() == LC_SEGMENT
|| cmd
->cmd() == LC_SEGMENT_64
) {
424 doseg(buffer
, (macho_segment_command
<P
>*)cmd
);
432 bool parse_macho(uint8_t *buffer
)
434 uint32_t magic
= *(uint32_t *)buffer
;
438 return parse_macho
<Pointer64
<CurrentEndian
>>(buffer
);
440 return parse_macho
<Pointer32
<CurrentEndian
>>(buffer
);
442 return parse_macho
<Pointer64
<OtherEndian
>>(buffer
);
444 return parse_macho
<Pointer32
<OtherEndian
>>(buffer
);
446 printf("file is not mach-o (magic %x)\n", magic
);
452 bool parse_fat(uint8_t *buffer
, size_t size
)
456 if (size
< sizeof(magic
)) {
457 printf("file is too small\n");
461 magic
= *(uint32_t *)buffer
;
462 if (magic
!= FAT_MAGIC
&& magic
!= FAT_CIGAM
) {
464 return parse_macho(buffer
);
466 struct fat_header
*fh
;
467 uint32_t fat_magic
, fat_nfat_arch
;
468 struct fat_arch
*archs
;
470 if (size
< sizeof(struct fat_header
)) {
471 printf("file is too small\n");
475 fh
= (struct fat_header
*)buffer
;
476 fat_magic
= OSSwapBigToHostInt32(fh
->magic
);
477 fat_nfat_arch
= OSSwapBigToHostInt32(fh
->nfat_arch
);
479 if (size
< (sizeof(struct fat_header
) + fat_nfat_arch
* sizeof(struct fat_arch
))) {
480 printf("file is too small\n");
484 archs
= (struct fat_arch
*)(buffer
+ sizeof(struct fat_header
));
486 /* Special case hidden CPU_TYPE_ARM64 */
487 if (size
>= (sizeof(struct fat_header
) + (fat_nfat_arch
+ 1) * sizeof(struct fat_arch
))) {
488 if (fat_nfat_arch
> 0
489 && OSSwapBigToHostInt32(archs
[fat_nfat_arch
].cputype
) == CPU_TYPE_ARM64
) {
493 /* End special case hidden CPU_TYPE_ARM64 */
495 if (debug
) printf("%d fat architectures\n",
498 for (uint32_t i
= 0; i
< fat_nfat_arch
; i
++) {
499 uint32_t arch_cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
500 uint32_t arch_cpusubtype
= OSSwapBigToHostInt32(archs
[i
].cpusubtype
);
501 uint32_t arch_offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
502 uint32_t arch_size
= OSSwapBigToHostInt32(archs
[i
].size
);
504 if (debug
) printf("cputype %d cpusubtype %d\n",
505 arch_cputype
, arch_cpusubtype
);
507 /* Check that slice data is after all fat headers and archs */
508 if (arch_offset
< (sizeof(struct fat_header
) + fat_nfat_arch
* sizeof(struct fat_arch
))) {
509 printf("file is badly formed\n");
513 /* Check that the slice ends before the file does */
514 if (arch_offset
> size
) {
515 printf("file is badly formed\n");
519 if (arch_size
> size
) {
520 printf("file is badly formed\n");
524 if (arch_offset
> (size
- arch_size
)) {
525 printf("file is badly formed\n");
529 bool ok
= parse_macho(buffer
+ arch_offset
);
530 if (!ok
) return false;
536 bool processFile(const char *filename
)
538 if (debug
) printf("file %s\n", filename
);
539 int fd
= open(filename
, O_RDWR
);
541 printf("open %s: %s\n", filename
, strerror(errno
));
546 if (fstat(fd
, &st
) < 0) {
547 printf("fstat %s: %s\n", filename
, strerror(errno
));
551 void *buffer
= mmap(NULL
, (size_t)st
.st_size
, PROT_READ
|PROT_WRITE
,
552 MAP_FILE
|MAP_SHARED
, fd
, 0);
553 if (buffer
== MAP_FAILED
) {
554 printf("mmap %s: %s\n", filename
, strerror(errno
));
558 bool result
= parse_fat((uint8_t *)buffer
, (size_t)st
.st_size
);
559 munmap(buffer
, (size_t)st
.st_size
);