]> git.saurik.com Git - apple/dyld.git/blob - launch-cache/MachOLayout.hpp
f49210dc2aa7a863823962d35772fcf2ecc473ae
[apple/dyld.git] / launch-cache / MachOLayout.hpp
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2006 Apple Computer, 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/mman.h>
31 #include <mach/mach.h>
32 #include <limits.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <unistd.h>
38 #include <mach-o/loader.h>
39 #include <mach-o/fat.h>
40
41 #include <vector>
42 #include <set>
43 #include <ext/hash_map>
44
45 #include "MachOFileAbstraction.hpp"
46 #include "Architectures.hpp"
47
48
49 void throwf(const char* format, ...) __attribute__((format(printf, 1, 2)));
50
51 __attribute__((noreturn))
52 void throwf(const char* format, ...)
53 {
54 va_list list;
55 char* p;
56 va_start(list, format);
57 vasprintf(&p, format, list);
58 va_end(list);
59
60 const char* t = p;
61 throw t;
62 }
63
64
65 class MachOLayoutAbstraction
66 {
67 public:
68 struct Segment
69 {
70 public:
71 Segment(uint64_t addr, uint64_t vmsize, uint64_t offset, uint64_t file_size,
72 uint32_t prot, const char* segName) : fAddress(addr), fSize(vmsize),
73 fFileOffset(offset), fFileSize(file_size), fPermissions(prot),
74 fNewAddress(0), fMappedAddress(NULL) {
75 strlcpy(fName, segName, 16);
76 }
77
78 uint64_t address() const { return fAddress; }
79 uint64_t size() const { return fSize; }
80 uint64_t fileOffset() const { return fFileOffset; }
81 uint64_t fileSize() const { return fFileSize; }
82 uint32_t permissions() const { return fPermissions; }
83 bool readable() const { return fPermissions & VM_PROT_READ; }
84 bool writable() const { return fPermissions & VM_PROT_WRITE; }
85 bool executable() const { return fPermissions & VM_PROT_EXECUTE; }
86 const char* name() const { return fName; }
87 uint64_t newAddress() const { return fNewAddress; }
88 void* mappedAddress() const { return fMappedAddress; }
89 void setNewAddress(uint64_t addr) { fNewAddress = addr; }
90 void setMappedAddress(void* addr) { fMappedAddress = addr; }
91 void setSize(uint64_t new_size) { fSize = new_size; }
92 void setFileOffset(uint64_t new_off) { fFileOffset = new_off; }
93 void setFileSize(uint64_t new_size) { fFileSize = new_size; }
94 void setWritable(bool writable) { if (writable) fPermissions |= VM_PROT_WRITE; else fPermissions &= ~VM_PROT_WRITE; }
95 private:
96 uint64_t fAddress;
97 uint64_t fSize;
98 uint64_t fFileOffset;
99 uint64_t fFileSize;
100 uint64_t fNewAddress;
101 void* fMappedAddress;
102 uint32_t fPermissions;
103 char fName[16];
104 };
105
106 struct Library
107 {
108 const char* name;
109 uint32_t currentVersion;
110 uint32_t compatibilityVersion;
111 };
112
113
114 virtual cpu_type_t getArchitecture() const = 0;
115 virtual const char* getFilePath() const = 0;
116 virtual uint64_t getOffsetInUniversalFile() const = 0;
117 virtual uint32_t getFileType() const = 0;
118 virtual uint32_t getFlags() const = 0;
119 virtual Library getID() const = 0;
120 virtual bool isSplitSeg() const = 0;
121 virtual bool hasSplitSegInfo() const = 0;
122 virtual uint32_t getNameFileOffset() const = 0;
123 virtual time_t getLastModTime() const = 0;
124 virtual ino_t getInode() const = 0;
125 virtual std::vector<Segment>& getSegments() = 0;
126 virtual const std::vector<Segment>& getSegments() const = 0;
127 virtual const std::vector<Library>& getLibraries() const = 0;
128 virtual uint64_t getBaseAddress() const = 0;
129 virtual uint64_t getVMSize() const = 0;
130 virtual uint64_t getBaseExecutableAddress() const = 0;
131 virtual uint64_t getBaseWritableAddress() const = 0;
132 virtual uint64_t getBaseReadOnlyAddress() const = 0;
133 virtual uint64_t getExecutableVMSize() const = 0;
134 virtual uint64_t getWritableVMSize() const = 0;
135 virtual uint64_t getReadOnlyVMSize() const = 0;
136 };
137
138
139
140
141 template <typename A>
142 class MachOLayout : public MachOLayoutAbstraction
143 {
144 public:
145 MachOLayout(const void* machHeader, uint64_t offset, const char* path, ino_t inode, time_t modTime);
146 virtual ~MachOLayout() {}
147
148 virtual cpu_type_t getArchitecture() const;
149 virtual const char* getFilePath() const { return fPath; }
150 virtual uint64_t getOffsetInUniversalFile() const { return fOffset; }
151 virtual uint32_t getFileType() const { return fFileType; }
152 virtual uint32_t getFlags() const { return fFlags; }
153 virtual Library getID() const { return fDylibID; }
154 virtual bool isSplitSeg() const;
155 virtual bool hasSplitSegInfo() const { return fHasSplitSegInfo; }
156 virtual uint32_t getNameFileOffset() const{ return fNameFileOffset; }
157 virtual time_t getLastModTime() const { return fMTime; }
158 virtual ino_t getInode() const { return fInode; }
159 virtual std::vector<Segment>& getSegments() { return fSegments; }
160 virtual const std::vector<Segment>& getSegments() const { return fSegments; }
161 virtual const std::vector<Library>& getLibraries() const { return fLibraries; }
162 virtual uint64_t getBaseAddress() const { return fLowSegment->address(); }
163 virtual uint64_t getVMSize() const { return fVMSize; }
164 virtual uint64_t getBaseExecutableAddress() const { return fLowExecutableSegment->address(); }
165 virtual uint64_t getBaseWritableAddress() const { return fLowWritableSegment->address(); }
166 virtual uint64_t getBaseReadOnlyAddress() const { return fLowReadOnlySegment->address(); }
167 virtual uint64_t getExecutableVMSize() const { return fVMExecutableSize; }
168 virtual uint64_t getWritableVMSize() const { return fVMWritablSize; }
169 virtual uint64_t getReadOnlyVMSize() const { return fVMReadOnlySize; }
170
171 private:
172 typedef typename A::P P;
173 typedef typename A::P::E E;
174 typedef typename A::P::uint_t pint_t;
175
176 const char* fPath;
177 uint64_t fOffset;
178 uint32_t fFileType;
179 uint32_t fFlags;
180 std::vector<Segment> fSegments;
181 std::vector<Library> fLibraries;
182 const Segment* fLowSegment;
183 const Segment* fLowExecutableSegment;
184 const Segment* fLowWritableSegment;
185 const Segment* fLowReadOnlySegment;
186 Library fDylibID;
187 uint32_t fNameFileOffset;
188 time_t fMTime;
189 ino_t fInode;
190 uint64_t fVMSize;
191 uint64_t fVMExecutableSize;
192 uint64_t fVMWritablSize;
193 uint64_t fVMReadOnlySize;
194 bool fHasSplitSegInfo;
195 };
196
197
198
199 class UniversalMachOLayout
200 {
201 public:
202 UniversalMachOLayout(const char* path, const std::set<cpu_type_t>* onlyArchs=NULL);
203 ~UniversalMachOLayout() {}
204
205 static const UniversalMachOLayout* find(const char* path, const std::set<cpu_type_t>* onlyArchs=NULL);
206 const MachOLayoutAbstraction* getArch(cpu_type_t) const;
207 const std::vector<MachOLayoutAbstraction*>& getArchs() const { return fLayouts; }
208
209 private:
210 struct CStringEquals {
211 bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
212 };
213 typedef __gnu_cxx::hash_map<const char*, const UniversalMachOLayout*, __gnu_cxx::hash<const char*>, CStringEquals> PathToNode;
214
215 static PathToNode fgLayoutCache;
216 const char* fPath;
217 std::vector<MachOLayoutAbstraction*> fLayouts;
218 };
219
220 UniversalMachOLayout::PathToNode UniversalMachOLayout::fgLayoutCache;
221
222
223 const MachOLayoutAbstraction* UniversalMachOLayout::getArch(cpu_type_t arch) const
224 {
225 for(std::vector<MachOLayoutAbstraction*>::const_iterator it=fLayouts.begin(); it != fLayouts.end(); ++it) {
226 const MachOLayoutAbstraction* layout = *it;
227 if ( layout->getArchitecture() == arch )
228 return layout;
229 }
230 return NULL;
231 }
232
233
234 const UniversalMachOLayout* UniversalMachOLayout::find(const char* path, const std::set<cpu_type_t>* onlyArchs)
235 {
236 // look in cache
237 PathToNode::iterator pos = fgLayoutCache.find(path);
238 if ( pos != fgLayoutCache.end() )
239 return pos->second;
240
241 // create UniversalMachOLayout
242 const UniversalMachOLayout* result = new UniversalMachOLayout(path, onlyArchs);
243
244 // add it to cache
245 fgLayoutCache[result->fPath] = result;
246
247 return result;
248 }
249
250
251 UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::set<cpu_type_t>* onlyArchs)
252 : fPath(strdup(path))
253 {
254 // map in whole file
255 int fd = ::open(path, O_RDONLY, 0);
256 if ( fd == -1 )
257 throwf("can't open file, errno=%d", errno);
258 struct stat stat_buf;
259 if ( fstat(fd, &stat_buf) == -1)
260 throwf("can't stat open file %s, errno=%d", path, errno);
261 if ( stat_buf.st_size < 20 )
262 throwf("file too small %s", path);
263 uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
264 if ( p == (uint8_t*)(-1) )
265 throwf("can't map file %s, errno=%d", path, errno);
266 ::close(fd);
267
268 try {
269 // if fat file, process each architecture
270 const fat_header* fh = (fat_header*)p;
271 const mach_header* mh = (mach_header*)p;
272 if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
273 // Fat header is always big-endian
274 const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
275 for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
276 uint32_t fileOffset = OSSwapBigToHostInt32(archs[i].offset);
277 cpu_type_t curArch = OSSwapBigToHostInt32(archs[i].cputype);
278 if ( fileOffset > stat_buf.st_size )
279 throwf("malformed universal file, slice for architecture 0x%08X is beyond end of file: %s", curArch, path);
280 try {
281 if ( (onlyArchs == NULL) || (onlyArchs->count(curArch) != 0) ) {
282 switch ( curArch ) {
283 case CPU_TYPE_POWERPC:
284 fLayouts.push_back(new MachOLayout<ppc>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime));
285 break;
286 case CPU_TYPE_POWERPC64:
287 fLayouts.push_back(new MachOLayout<ppc64>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime));
288 break;
289 case CPU_TYPE_I386:
290 fLayouts.push_back(new MachOLayout<x86>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime));
291 break;
292 case CPU_TYPE_X86_64:
293 fLayouts.push_back(new MachOLayout<x86_64>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime));
294 break;
295 default:
296 throw "unknown file format";
297 }
298 }
299 }
300 catch (const char* msg) {
301 fprintf(stderr, "warning: %s for %s\n", msg, path);
302 }
303 }
304 }
305 else {
306 try {
307 if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC)) {
308 if ( (onlyArchs == NULL) || (onlyArchs->count(CPU_TYPE_POWERPC) != 0) )
309 fLayouts.push_back(new MachOLayout<ppc>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime));
310 }
311 else if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC64)) {
312 if ( (onlyArchs == NULL) || (onlyArchs->count(CPU_TYPE_POWERPC64) != 0) )
313 fLayouts.push_back(new MachOLayout<ppc64>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime));
314 }
315 else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) {
316 if ( (onlyArchs == NULL) || (onlyArchs->count(CPU_TYPE_I386) != 0) )
317 fLayouts.push_back(new MachOLayout<x86>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime));
318 }
319 else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) {
320 if ( (onlyArchs == NULL) || (onlyArchs->count(CPU_TYPE_X86_64) != 0) )
321 fLayouts.push_back(new MachOLayout<x86_64>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime));
322 }
323 else {
324 throw "unknown file format";
325 }
326 }
327 catch (const char* msg) {
328 fprintf(stderr, "warning: %s for %s\n", msg, path);
329 }
330 }
331 }
332 catch (...) {
333 ::munmap(p, stat_buf.st_size);
334 throw;
335 }
336 }
337
338
339
340 template <typename A>
341 MachOLayout<A>::MachOLayout(const void* machHeader, uint64_t offset, const char* path, ino_t inode, time_t modTime)
342 : fPath(path), fOffset(offset), fMTime(modTime), fInode(inode), fHasSplitSegInfo(false)
343 {
344 fDylibID.name = NULL;
345 fDylibID.currentVersion = 0;
346 fDylibID.compatibilityVersion = 0;
347
348 const macho_header<P>* mh = (const macho_header<P>*)machHeader;
349 if ( mh->cputype() != getArchitecture() )
350 throw "wrong architecture";
351 switch ( mh->filetype() ) {
352 case MH_DYLIB:
353 case MH_BUNDLE:
354 case MH_EXECUTE:
355 case MH_DYLIB_STUB:
356 case MH_DYLINKER:
357 break;
358 default:
359 throw "file is not a mach-o final linked image";
360 }
361 fFlags = mh->flags();
362 fFileType = mh->filetype();
363
364 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
365 const uint32_t cmd_count = mh->ncmds();
366 const macho_load_command<P>* cmd = cmds;
367 for (uint32_t i = 0; i < cmd_count; ++i) {
368 switch ( cmd->cmd() ) {
369 case LC_ID_DYLIB:
370 {
371 macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
372 fDylibID.name = strdup(dylib->name());
373 fDylibID.currentVersion = dylib->current_version();
374 fDylibID.compatibilityVersion = dylib->compatibility_version();
375 fNameFileOffset = dylib->name() - (char*)machHeader;
376 }
377 break;
378 case LC_LOAD_DYLIB:
379 case LC_LOAD_WEAK_DYLIB:
380 case LC_REEXPORT_DYLIB:
381 {
382 macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
383 Library lib;
384 lib.name = strdup(dylib->name());
385 lib.currentVersion = dylib->current_version();
386 lib.compatibilityVersion = dylib->compatibility_version();
387 fLibraries.push_back(lib);
388 }
389 break;
390 case LC_SEGMENT_SPLIT_INFO:
391 fHasSplitSegInfo = true;
392 break;
393 case macho_segment_command<P>::CMD:
394 {
395 macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
396 fSegments.push_back(Segment(segCmd->vmaddr(), segCmd->vmsize(), segCmd->fileoff(),
397 segCmd->filesize(), segCmd->initprot(), segCmd->segname()));
398 }
399 break;
400 }
401 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
402 }
403
404 fLowSegment = NULL;
405 fLowExecutableSegment = NULL;
406 fLowWritableSegment = NULL;
407 fLowReadOnlySegment = NULL;
408 fVMExecutableSize = 0;
409 fVMWritablSize = 0;
410 fVMReadOnlySize = 0;
411 fVMSize = 0;
412 const Segment* highSegment = NULL;
413 for(std::vector<Segment>::const_iterator it = fSegments.begin(); it != fSegments.end(); ++it) {
414 const Segment& seg = *it;
415 if ( (fLowSegment == NULL) || (seg.address() < fLowSegment->address()) )
416 fLowSegment = &seg;
417 if ( (highSegment == NULL) || (seg.address() > highSegment->address()) )
418 highSegment = &seg;
419 if ( seg.executable() ) {
420 if ( (fLowExecutableSegment == NULL) || (seg.address() < fLowExecutableSegment->address()) )
421 fLowExecutableSegment = &seg;
422 fVMExecutableSize += seg.size();
423 }
424 else if ( seg.writable()) {
425 if ( (fLowWritableSegment == NULL) || (seg.address() < fLowWritableSegment->address()) )
426 fLowWritableSegment = &seg;
427 fVMWritablSize += seg.size();
428 }
429 else {
430 if ( (fLowReadOnlySegment == NULL) || (seg.address() < fLowReadOnlySegment->address()) )
431 fLowReadOnlySegment = &seg;
432 fVMReadOnlySize += seg.size();
433 }
434 }
435 if ( (highSegment != NULL) && (fLowSegment != NULL) )
436 fVMSize = (highSegment->address() + highSegment->size() - fLowSegment->address() + 4095) & (-4096);
437 }
438
439 template <> cpu_type_t MachOLayout<ppc>::getArchitecture() const { return CPU_TYPE_POWERPC; }
440 template <> cpu_type_t MachOLayout<ppc64>::getArchitecture() const { return CPU_TYPE_POWERPC64; }
441 template <> cpu_type_t MachOLayout<x86>::getArchitecture() const { return CPU_TYPE_I386; }
442 template <> cpu_type_t MachOLayout<x86_64>::getArchitecture() const { return CPU_TYPE_X86_64; }
443
444 template <>
445 bool MachOLayout<ppc>::isSplitSeg() const
446 {
447 return ( (this->getFlags() & MH_SPLIT_SEGS) != 0 );
448 }
449
450 template <>
451 bool MachOLayout<x86>::isSplitSeg() const
452 {
453 return ( (this->getFlags() & MH_SPLIT_SEGS) != 0 );
454 }
455
456 template <typename A>
457 bool MachOLayout<A>::isSplitSeg() const
458 {
459 return false;
460 }
461
462
463 #endif // __MACHO_LAYOUT__
464
465
466