1/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2006-2011 Apple Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25#ifndef __MACHO_LAYOUT__
26#define __MACHO_LAYOUT__
27
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <sys/errno.h>
31#include <sys/mman.h>
32#include <mach/mach.h>
33#include <limits.h>
34#include <stdarg.h>
35#include <stdio.h>
36#include <fcntl.h>
37#include <errno.h>
38#include <unistd.h>
39#include <mach-o/loader.h>
40#include <mach-o/fat.h>
41
42#include <vector>
43#include <set>
44#include <unordered_map>
45
46#include "MachOFileAbstraction.hpp"
47#include "Architectures.hpp"
48
49
50void throwf(const char* format, ...) __attribute__((format(printf, 1, 2)));
51
52__attribute__((noreturn))
53void throwf(const char* format, ...)
54{
55	va_list	list;
56	char*	p;
57	va_start(list, format);
58	vasprintf(&p, format, list);
59	va_end(list);
60
61	const char*	t = p;
62	throw t;
63}
64
65
66class MachOLayoutAbstraction
67{
68public:
69	struct Segment
70	{
71	public:
72					Segment(uint64_t addr, uint64_t vmsize, uint64_t offset, uint64_t file_size, uint64_t align,
73							uint32_t prot, const char* segName) : fOrigAddress(addr), fOrigSize(vmsize),
74							fOrigFileOffset(offset),  fOrigFileSize(file_size), fOrigPermissions(prot),
75							fSize(vmsize), fFileOffset(offset), fFileSize(file_size), fAlignment(align),
76							fPermissions(prot), fNewAddress(0), fMappedAddress(NULL) {
77								strlcpy(fOrigName, segName, 16);
78							}
79
80		uint64_t	address() const		{ return fOrigAddress; }
81		uint64_t	size() const		{ return fSize; }
82		uint64_t	fileOffset() const	{ return fFileOffset; }
83		uint64_t	fileSize() const	{ return fFileSize; }
84		uint32_t	permissions() const { return fPermissions; }
85		bool		readable() const	{ return fPermissions & VM_PROT_READ; }
86		bool		writable() const	{ return fPermissions & VM_PROT_WRITE; }
87		bool		executable() const	{ return fPermissions & VM_PROT_EXECUTE; }
88		uint64_t	alignment() const	{ return fAlignment; }
89		const char* name() const		{ return fOrigName; }
90		uint64_t	newAddress() const	{ return fNewAddress; }
91		void*		mappedAddress() const			{ return fMappedAddress; }
92		void		setNewAddress(uint64_t addr)	{ fNewAddress = addr; }
93		void		setMappedAddress(void* addr)	{ fMappedAddress = addr; }
94		void		setSize(uint64_t new_size)		{ fSize = new_size; }
95		void		setFileOffset(uint64_t new_off)	{ fFileOffset = new_off; }
96		void		setFileSize(uint64_t new_size)	{ fFileSize = new_size; }
97		void		setWritable(bool w)				{ if (w) fPermissions |= VM_PROT_WRITE; else fPermissions &= ~VM_PROT_WRITE; }
98		void		reset()							{ fSize=fOrigSize; fFileOffset=fOrigFileOffset; fFileSize=fOrigFileSize; fPermissions=fOrigPermissions; }
99	private:
100		uint64_t		fOrigAddress;
101		uint64_t		fOrigSize;
102		uint64_t		fOrigFileOffset;
103		uint64_t		fOrigFileSize;
104		uint32_t		fOrigPermissions;
105		char			fOrigName[16];
106		uint64_t		fSize;
107		uint64_t		fFileOffset;
108		uint64_t		fFileSize;
109		uint64_t		fAlignment;
110		uint32_t		fPermissions;
111		uint64_t		fNewAddress;
112		void*			fMappedAddress;
113	};
114
115	struct Library
116	{
117		const char*	name;
118		uint32_t	currentVersion;
119		uint32_t	compatibilityVersion;
120		bool		weakImport;
121	};
122
123
124	virtual ArchPair							getArchPair() const = 0;
125	virtual const char*							getFilePath() const = 0;
126	virtual uint64_t							getOffsetInUniversalFile() const	= 0;
127	virtual uint32_t							getFileType() const	= 0;
128	virtual uint32_t							getFlags() const = 0;
129	virtual	Library								getID() const = 0;
130	virtual bool								isDylib() const = 0;
131	virtual bool								isSplitSeg() const = 0;
132	virtual bool								hasSplitSegInfo() const = 0;
133	virtual bool								isRootOwned() const = 0;
134	virtual bool								inSharableLocation() const = 0;
135	virtual bool								hasDynamicLookupLinkage() const = 0;
136	virtual bool								hasMainExecutableLookupLinkage() const = 0;
137	virtual bool								isTwoLevelNamespace() const	= 0;
138	virtual bool								hasDyldInfo() const	= 0;
139	virtual bool								hasMultipleReadWriteSegments() const = 0;
140	virtual	uint32_t							getNameFileOffset() const = 0;
141	virtual time_t								getLastModTime() const = 0;
142	virtual ino_t								getInode() const = 0;
143	virtual std::vector<Segment>&				getSegments() = 0;
144	virtual const std::vector<Segment>&			getSegments() const = 0;
145	virtual const Segment*						getSegment(const char* name) const = 0;
146	virtual const std::vector<Library>&			getLibraries() const = 0;
147	virtual uint64_t							getBaseAddress() const = 0;
148	virtual uint64_t							getVMSize() const = 0;
149	virtual uint64_t							getBaseExecutableAddress() const = 0;
150	virtual uint64_t							getBaseWritableAddress() const = 0;
151	virtual uint64_t							getBaseReadOnlyAddress() const = 0;
152	virtual uint64_t							getExecutableVMSize() const = 0;
153	virtual uint64_t							getWritableVMSize() const = 0;
154	virtual uint64_t							getReadOnlyVMSize() const = 0;
155	// need getDyldInfoExports because export info uses ULEB encoding and size could grow
156	virtual const uint8_t*						getDyldInfoExports() const = 0;
157	virtual void								setDyldInfoExports(const uint8_t* newExports) const = 0;
158	virtual void								uuid(uuid_t u) const = 0;
159};
160
161
162
163
164template <typename A>
165class MachOLayout : public MachOLayoutAbstraction
166{
167public:
168												MachOLayout(const void* machHeader, uint64_t offset, const char* path,
169																	ino_t inode, time_t modTime, uid_t uid);
170	virtual										~MachOLayout() {}
171
172	virtual ArchPair							getArchPair() const		{ return fArchPair; }
173	virtual const char*							getFilePath() const		{ return fPath; }
174	virtual uint64_t							getOffsetInUniversalFile() const { return fOffset; }
175	virtual uint32_t							getFileType() const		{ return fFileType; }
176	virtual uint32_t							getFlags() const		{ return fFlags; }
177	virtual	Library								getID() const			{ return fDylibID; }
178	virtual bool								isDylib() const			{ return fIsDylib; }
179	virtual bool								isSplitSeg() const;
180	virtual bool								hasSplitSegInfo() const	{ return fHasSplitSegInfo; }
181	virtual bool								isRootOwned() const		{ return fRootOwned; }
182	virtual bool								inSharableLocation() const { return fShareableLocation; }
183	virtual bool								hasDynamicLookupLinkage() const { return fDynamicLookupLinkage; }
184	virtual bool								hasMainExecutableLookupLinkage() const { return fMainExecutableLookupLinkage; }
185	virtual bool								isTwoLevelNamespace() const	{ return (fFlags & MH_TWOLEVEL); }
186	virtual bool								hasDyldInfo() const		{ return fHasDyldInfo; }
187	virtual bool								hasMultipleReadWriteSegments() const { return fHasTooManyWritableSegments; }
188	virtual	uint32_t							getNameFileOffset() const{ return fNameFileOffset; }
189	virtual time_t								getLastModTime() const	{ return fMTime; }
190	virtual ino_t								getInode() const		{ return fInode; }
191	virtual std::vector<Segment>&				getSegments()			{ return fSegments; }
192	virtual const std::vector<Segment>&			getSegments() const		{ return fSegments; }
193	virtual const Segment*						getSegment(const char* name) const;
194	virtual const std::vector<Library>&			getLibraries() const	{ return fLibraries; }
195	virtual uint64_t							getBaseAddress() const	{ return fLowSegment->address(); }
196	virtual uint64_t							getVMSize() const		{ return fVMSize; }
197	virtual uint64_t							getBaseExecutableAddress() const { return fLowExecutableSegment->address(); }
198	virtual uint64_t							getBaseWritableAddress() const	{ return fLowWritableSegment->address(); }
199	virtual uint64_t							getBaseReadOnlyAddress() const	{ return fLowReadOnlySegment->address(); }
200	virtual uint64_t							getExecutableVMSize() const		{ return fVMExecutableSize; }
201	virtual uint64_t							getWritableVMSize() const		{ return fVMWritablSize; }
202	virtual uint64_t							getReadOnlyVMSize() const		{ return fVMReadOnlySize; }
203	virtual const uint8_t*						getDyldInfoExports() const		{ return fDyldInfoExports; }
204	virtual void								setDyldInfoExports(const uint8_t* newExports) const { fDyldInfoExports = newExports; }
205	virtual void								uuid(uuid_t u) const { memcpy(u, fUUID, 16); }
206
207private:
208	typedef typename A::P					P;
209	typedef typename A::P::E				E;
210	typedef typename A::P::uint_t			pint_t;
211
212	uint64_t									segmentSize(const macho_segment_command<typename A::P>* segCmd) const;
213	uint64_t									segmentFileSize(const macho_segment_command<typename A::P>* segCmd) const;
214	uint64_t									segmentAlignment(const macho_segment_command<typename A::P>* segCmd) const;
215	bool										validReadWriteSeg(const Segment& seg) const;
216
217	static cpu_type_t							arch();
218
219	const char*									fPath;
220	uint64_t									fOffset;
221	uint32_t									fFileType;
222	ArchPair									fArchPair;
223	uint32_t									fFlags;
224	std::vector<Segment>						fSegments;
225	std::vector<Library>						fLibraries;
226	const Segment*								fLowSegment;
227	const Segment*								fLowExecutableSegment;
228	const Segment*								fLowWritableSegment;
229	const Segment*								fLowReadOnlySegment;
230	Library										fDylibID;
231	uint32_t									fNameFileOffset;
232	time_t										fMTime;
233	ino_t										fInode;
234	uint64_t									fVMSize;
235	uint64_t									fVMExecutableSize;
236	uint64_t									fVMWritablSize;
237	uint64_t									fVMReadOnlySize;
238	bool										fHasSplitSegInfo;
239	bool										fRootOwned;
240	bool										fShareableLocation;
241	bool										fDynamicLookupLinkage;
242	bool										fMainExecutableLookupLinkage;
243	bool										fIsDylib;
244	bool										fHasDyldInfo;
245	bool										fHasTooManyWritableSegments;
246	mutable const uint8_t*						fDyldInfoExports;
247	uuid_t										fUUID;
248};
249
250
251
252class UniversalMachOLayout
253{
254public:
255												UniversalMachOLayout(const char* path, const std::set<ArchPair>* onlyArchs=NULL);
256												~UniversalMachOLayout() {}
257
258	static const UniversalMachOLayout&			find(const char* path, const std::set<ArchPair>* onlyArchs=NULL);
259	const MachOLayoutAbstraction*				getSlice(ArchPair ap) const;
260	const std::vector<MachOLayoutAbstraction*>&	allLayouts() const { return fLayouts; }
261
262private:
263	class CStringHash {
264	public:
265		size_t operator()(const char* __s) const {
266			size_t __h = 0;
267			for ( ; *__s; ++__s)
268				__h = 5 * __h + *__s;
269			return __h;
270		};
271	};
272	struct CStringEquals {
273		bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
274	};
275	typedef std::unordered_map<const char*, const UniversalMachOLayout*, CStringHash, CStringEquals> PathToNode;
276
277	static bool					requestedSlice(const std::set<ArchPair>* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType);
278
279	static PathToNode							fgLayoutCache;
280	const char*									fPath;
281	std::vector<MachOLayoutAbstraction*>		fLayouts;
282};
283
284UniversalMachOLayout::PathToNode UniversalMachOLayout::fgLayoutCache;
285
286
287
288
289const MachOLayoutAbstraction* UniversalMachOLayout::getSlice(ArchPair ap) const
290{
291	// use matching cputype and cpusubtype
292	for(std::vector<MachOLayoutAbstraction*>::const_iterator it=fLayouts.begin(); it != fLayouts.end(); ++it) {
293		const MachOLayoutAbstraction* layout = *it;
294		if ( layout->getArchPair().arch == ap.arch ) {
295            switch ( ap.arch ) {
296                case CPU_TYPE_ARM:
297				case CPU_TYPE_X86_64:
298                   if ( (layout->getArchPair().subtype & ~CPU_SUBTYPE_MASK) == (ap.subtype & ~CPU_SUBTYPE_MASK) )
299                        return layout;
300                    break;
301                 default:
302                    return layout;
303            }
304        }
305	}
306	// if requesting x86_64h and it did not exist, try x86_64 as a fallback
307	if ((ap.arch == CPU_TYPE_X86_64) && (ap.subtype == CPU_SUBTYPE_X86_64_H)) {
308		ap.subtype = CPU_SUBTYPE_X86_64_ALL;
309		return this->getSlice(ap);
310	}
311	return NULL;
312}
313
314
315const UniversalMachOLayout& UniversalMachOLayout::find(const char* path, const std::set<ArchPair>* onlyArchs)
316{
317	// look in cache
318	PathToNode::iterator pos = fgLayoutCache.find(path);
319	if ( pos != fgLayoutCache.end() )
320		return *pos->second;
321
322	// create UniversalMachOLayout
323	const UniversalMachOLayout* result = new UniversalMachOLayout(path, onlyArchs);
324
325	// add it to cache
326	fgLayoutCache[result->fPath] = result;
327
328	return *result;
329}
330
331
332bool UniversalMachOLayout::requestedSlice(const std::set<ArchPair>* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType)
333{
334	if ( onlyArchs == NULL )
335		return true;
336	// must match cputype and cpusubtype
337	for (std::set<ArchPair>::const_iterator it = onlyArchs->begin(); it != onlyArchs->end(); ++it) {
338		ArchPair anArch = *it;
339		if ( cpuType == anArch.arch ) {
340            switch ( cpuType ) {
341                case CPU_TYPE_ARM:
342                    if ( cpuSubType == anArch.subtype )
343                        return true;
344                    break;
345                default:
346                    return true;
347            }
348        }
349	}
350	return false;
351}
352
353
354UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::set<ArchPair>* onlyArchs)
355 : fPath(strdup(path))
356{
357	// map in whole file
358	int fd = ::open(path, O_RDONLY, 0);
359	if ( fd == -1 ) {
360		int err = errno;
361		if  ( err == ENOENT )
362			throwf("file not found");
363		else
364			throwf("can't open file, errno=%d", err);
365	}
366	struct stat stat_buf;
367	if ( fstat(fd, &stat_buf) == -1)
368		throwf("can't stat open file %s, errno=%d", path, errno);
369	if ( stat_buf.st_size < 20 )
370		throwf("file too small %s", path);
371	uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
372	if ( p == (uint8_t*)(-1) )
373		throwf("can't map file %s, errno=%d", path, errno);
374	::close(fd);
375
376	try {
377		// if fat file, process each architecture
378		const fat_header* fh = (fat_header*)p;
379		const mach_header* mh = (mach_header*)p;
380		if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
381			// Fat header is always big-endian
382			const struct fat_arch* slices = (struct fat_arch*)(p + sizeof(struct fat_header));
383			const uint32_t sliceCount = OSSwapBigToHostInt32(fh->nfat_arch);
384			for (uint32_t i=0; i < sliceCount; ++i) {
385				if ( requestedSlice(onlyArchs, OSSwapBigToHostInt32(slices[i].cputype), OSSwapBigToHostInt32(slices[i].cpusubtype)) ) {
386					uint32_t fileOffset = OSSwapBigToHostInt32(slices[i].offset);
387					if ( fileOffset > stat_buf.st_size ) {
388						throwf("malformed universal file, slice %u for architecture 0x%08X is beyond end of file: %s",
389								i, OSSwapBigToHostInt32(slices[i].cputype), path);
390					}
391					if ( (fileOffset+OSSwapBigToHostInt32(slices[i].size)) > stat_buf.st_size ) {
392						throwf("malformed universal file, slice %u for architecture 0x%08X is beyond end of file: %s",
393								i, OSSwapBigToHostInt32(slices[i].cputype), path);
394					}
395					try {
396						switch ( OSSwapBigToHostInt32(slices[i].cputype) ) {
397							case CPU_TYPE_I386:
398								fLayouts.push_back(new MachOLayout<x86>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
399								break;
400							case CPU_TYPE_X86_64:
401								fLayouts.push_back(new MachOLayout<x86_64>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
402								break;
403							case CPU_TYPE_ARM:
404								fLayouts.push_back(new MachOLayout<arm>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
405								break;
406							case CPU_TYPE_ARM64:
407								fLayouts.push_back(new MachOLayout<arm64>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
408								break;
409							default:
410								throw "unknown slice in fat file";
411						}
412					}
413					catch (const char* msg) {
414						fprintf(stderr, "warning: %s for %s\n", msg, path);
415					}
416				}
417			}
418		}
419		else {
420			try {
421				if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) {
422					if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
423						fLayouts.push_back(new MachOLayout<x86>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
424				}
425				else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) {
426					if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
427						fLayouts.push_back(new MachOLayout<x86_64>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
428				}
429				else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM)) {
430					if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
431						fLayouts.push_back(new MachOLayout<arm>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
432				}
433				else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM64)) {
434					if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
435						fLayouts.push_back(new MachOLayout<arm64>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
436				}
437				else {
438					throw "unknown file format";
439				}
440			}
441			catch (const char* msg) {
442				fprintf(stderr, "warning: %s for %s\n", msg, path);
443			}
444		}
445	}
446	catch (...) {
447		::munmap(p, stat_buf.st_size);
448		throw;
449	}
450}
451
452
453template <typename A>
454uint64_t MachOLayout<A>::segmentSize(const macho_segment_command<typename A::P>* segCmd) const
455{
456	// <rdar://problem/13089366> segments may have 16KB alignment padding at end, if so we can remove that in cache
457	if ( segCmd->nsects() > 0 ) {
458		const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
459		const macho_section<P>* const lastSection = &sectionsStart[segCmd->nsects()-1];
460		uint64_t endSectAddr = lastSection->addr() + lastSection->size();
461		uint64_t endSectAddrPage = (endSectAddr + 4095) & (-4096);
462		if ( endSectAddrPage < (segCmd->vmaddr() + segCmd->vmsize()) ) {
463			uint64_t size =  endSectAddrPage - segCmd->vmaddr();
464			//if ( size != segCmd->vmsize() )
465			//	fprintf(stderr, "trim %s size=0x%08llX instead of 0x%08llX for %s\n",
466			//		segCmd->segname(), size, segCmd->vmsize(), getFilePath());
467			return size;
468		}
469	}
470	return segCmd->vmsize();
471}
472
473template <typename A>
474uint64_t MachOLayout<A>::segmentAlignment(const macho_segment_command<typename A::P>* segCmd) const
475{
476	int p2align = 12;
477	if ( segCmd->nsects() > 0 ) {
478		const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
479		const macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()-1];
480		for (const macho_section<P>*  sect=sectionsStart; sect < sectionsEnd; ++sect) {
481			if ( sect->align() > p2align )
482				p2align = sect->align();
483		}
484	}
485	return (1 << p2align);
486}
487
488template <typename A>
489uint64_t MachOLayout<A>::segmentFileSize(const macho_segment_command<typename A::P>* segCmd) const
490{
491	// <rdar://problem/13089366> segments may have 16KB alignment padding at end, if so we can remove that in cache
492	if ( segCmd->nsects() > 0 ) {
493		uint64_t endOffset = segCmd->fileoff();
494		const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
495		const macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
496		for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
497			if ( sect->offset() != 0 )
498				endOffset = sect->offset() + sect->size();
499		}
500		uint64_t size = (endOffset - segCmd->fileoff() + 4095) & (-4096);
501		//if ( size != segCmd->filesize() )
502		//	fprintf(stderr, "trim %s filesize=0x%08llX instead of 0x%08llX for %s\n",
503		//		segCmd->segname(), size, segCmd->filesize(), getFilePath());
504		return size;
505	}
506	return segCmd->filesize();
507}
508
509
510template <typename A>
511MachOLayout<A>::MachOLayout(const void* machHeader, uint64_t offset, const char* path, ino_t inode, time_t modTime, uid_t uid)
512 : fPath(path), fOffset(offset), fArchPair(0,0), fMTime(modTime), fInode(inode), fHasSplitSegInfo(false), fRootOwned(uid==0),
513   fShareableLocation(false), fDynamicLookupLinkage(false), fMainExecutableLookupLinkage(false), fIsDylib(false),
514	fHasDyldInfo(false), fHasTooManyWritableSegments(false), fDyldInfoExports(NULL)
515{
516	fDylibID.name = NULL;
517	fDylibID.currentVersion = 0;
518	fDylibID.compatibilityVersion = 0;
519	bzero(fUUID, sizeof(fUUID));
520
521	const macho_header<P>* mh = (const macho_header<P>*)machHeader;
522	if ( mh->cputype() != arch() )
523		throw "Layout object is wrong architecture";
524	switch ( mh->filetype() ) {
525		case MH_DYLIB:
526			fIsDylib = true;
527			break;
528		case MH_BUNDLE:
529		case MH_EXECUTE:
530		case MH_DYLIB_STUB:
531		case MH_DYLINKER:
532			break;
533		default:
534			throw "file is not a mach-o final linked image";
535	}
536	fFlags = mh->flags();
537	fFileType = mh->filetype();
538	fArchPair.arch = mh->cputype();
539	fArchPair.subtype = mh->cpusubtype();
540
541	const macho_dyld_info_command<P>* dyldInfo = NULL;
542	const macho_symtab_command<P>* symbolTableCmd = NULL;
543	const macho_dysymtab_command<P>* dynamicSymbolTableCmd = NULL;
544	const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
545	const uint32_t cmd_count = mh->ncmds();
546	const macho_load_command<P>* cmd = cmds;
547	for (uint32_t i = 0; i < cmd_count; ++i) {
548		switch ( cmd->cmd() ) {
549			case LC_ID_DYLIB:
550				{
551					macho_dylib_command<P>* dylib  = (macho_dylib_command<P>*)cmd;
552					fDylibID.name = strdup(dylib->name());
553					fDylibID.currentVersion = dylib->current_version();
554					fDylibID.compatibilityVersion = dylib->compatibility_version();
555					fNameFileOffset = dylib->name() - (char*)machHeader;
556					fShareableLocation = ( (strncmp(fDylibID.name, "/usr/lib/", 9) == 0) || (strncmp(fDylibID.name, "/System/Library/", 16) == 0) );
557				}
558				break;
559			case LC_LOAD_DYLIB:
560			case LC_LOAD_WEAK_DYLIB:
561			case LC_REEXPORT_DYLIB:
562			case LC_LOAD_UPWARD_DYLIB:
563				{
564					macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
565					Library lib;
566					lib.name = strdup(dylib->name());
567					lib.currentVersion = dylib->current_version();
568					lib.compatibilityVersion = dylib->compatibility_version();
569					lib.weakImport = ( cmd->cmd() == LC_LOAD_WEAK_DYLIB );
570					fLibraries.push_back(lib);
571				}
572				break;
573			case LC_SEGMENT_SPLIT_INFO:
574				fHasSplitSegInfo = true;
575				break;
576			case macho_segment_command<P>::CMD:
577				{
578					const macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
579					fSegments.push_back(Segment(segCmd->vmaddr(), segmentSize(segCmd), segCmd->fileoff(),
580								segmentFileSize(segCmd), segmentAlignment(segCmd), segCmd->initprot(), segCmd->segname()));
581				}
582				break;
583			case LC_SYMTAB:
584				symbolTableCmd = (macho_symtab_command<P>*)cmd;
585				break;
586			case LC_DYSYMTAB:
587				dynamicSymbolTableCmd = (macho_dysymtab_command<P>*)cmd;
588				break;
589			case LC_DYLD_INFO:
590			case LC_DYLD_INFO_ONLY:
591				fHasDyldInfo = true;
592				dyldInfo = (struct macho_dyld_info_command<P>*)cmd;
593				break;
594			case LC_UUID:
595				{
596					const macho_uuid_command<P>* uc = (macho_uuid_command<P>*)cmd;
597					memcpy(&fUUID, uc->uuid(), 16);
598				}
599				break;
600		}
601		cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
602	}
603
604	fLowSegment = NULL;
605	fLowExecutableSegment = NULL;
606	fLowWritableSegment = NULL;
607	fLowReadOnlySegment = NULL;
608	fVMExecutableSize = 0;
609	fVMWritablSize = 0;
610	fVMReadOnlySize = 0;
611	fVMSize = 0;
612	const Segment* highSegment = NULL;
613	for(std::vector<Segment>::const_iterator it = fSegments.begin(); it != fSegments.end(); ++it) {
614		const Segment& seg = *it;
615		if ( (fLowSegment == NULL) || (seg.address() < fLowSegment->address()) )
616			fLowSegment = &seg;
617		if ( (highSegment == NULL) || (seg.address() > highSegment->address()) )
618			highSegment = &seg;
619		if ( seg.executable() ) {
620			if ( (fLowExecutableSegment == NULL) || (seg.address() < fLowExecutableSegment->address()) )
621				fLowExecutableSegment = &seg;
622			fVMExecutableSize += seg.size();
623		}
624		else if ( seg.writable()) {
625			if ( (fLowWritableSegment == NULL) || (seg.address() < fLowWritableSegment->address()) )
626				fLowWritableSegment = &seg;
627			fVMWritablSize += seg.size();
628			if ( !validReadWriteSeg(seg) ) {
629				fHasTooManyWritableSegments = true;
630			}
631		}
632		else {
633			if ( (fLowReadOnlySegment == NULL) || (seg.address() < fLowReadOnlySegment->address()) )
634				fLowReadOnlySegment = &seg;
635			fVMReadOnlySize += seg.size();
636		}
637	}
638	if ( (highSegment != NULL) && (fLowSegment != NULL) )
639		fVMSize = (highSegment->address() + highSegment->size() - fLowSegment->address() + 4095) & (-4096);
640
641	// scan undefines looking, for magic ordinals
642	if ( (symbolTableCmd != NULL) && (dynamicSymbolTableCmd != NULL) ) {
643		const macho_nlist<P>* symbolTable = (macho_nlist<P>*)((uint8_t*)machHeader + symbolTableCmd->symoff());
644		const uint32_t startUndefs = dynamicSymbolTableCmd->iundefsym();
645		const uint32_t endUndefs = startUndefs + dynamicSymbolTableCmd->nundefsym();
646		for (uint32_t i=startUndefs; i < endUndefs; ++i) {
647			uint8_t ordinal = GET_LIBRARY_ORDINAL(symbolTable[i].n_desc());
648			if ( ordinal == DYNAMIC_LOOKUP_ORDINAL )
649				fDynamicLookupLinkage = true;
650			else if ( ordinal == EXECUTABLE_ORDINAL )
651				fMainExecutableLookupLinkage = true;
652		}
653	}
654
655	if ( dyldInfo != NULL ) {
656		if ( dyldInfo->export_off() != 0 ) {
657			fDyldInfoExports = (uint8_t*)machHeader + dyldInfo->export_off();
658		}
659	}
660
661}
662
663template <> cpu_type_t MachOLayout<x86>::arch()     { return CPU_TYPE_I386; }
664template <> cpu_type_t MachOLayout<x86_64>::arch()  { return CPU_TYPE_X86_64; }
665template <> cpu_type_t MachOLayout<arm>::arch()		{ return CPU_TYPE_ARM; }
666template <> cpu_type_t MachOLayout<arm64>::arch()	{ return CPU_TYPE_ARM64; }
667
668template <>
669bool MachOLayout<x86>::validReadWriteSeg(const Segment& seg) const
670{
671	return (strcmp(seg.name(), "__DATA") == 0) || (strcmp(seg.name(), "__OBJC") == 0);
672}
673
674template <typename A>
675bool MachOLayout<A>::validReadWriteSeg(const Segment& seg) const
676{
677	return (strcmp(seg.name(), "__DATA") == 0);
678}
679
680
681template <>
682bool MachOLayout<x86>::isSplitSeg() const
683{
684	return ( (this->getFlags() & MH_SPLIT_SEGS) != 0 );
685}
686
687template <>
688bool MachOLayout<arm>::isSplitSeg() const
689{
690	return ( (this->getFlags() & MH_SPLIT_SEGS) != 0 );
691}
692
693template <typename A>
694bool MachOLayout<A>::isSplitSeg() const
695{
696	return false;
697}
698
699template <typename A>
700const MachOLayoutAbstraction::Segment* MachOLayout<A>::getSegment(const char* name) const
701{
702	for(std::vector<Segment>::const_iterator it = fSegments.begin(); it != fSegments.end(); ++it) {
703		const Segment& seg = *it;
704		if ( strcmp(seg.name(), name) == 0 )
705			return &seg;
706	}
707	return NULL;
708}
709
710
711
712#endif // __MACHO_LAYOUT__
713
714
715
716