1/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2008 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
26#include <string.h>
27#include <fcntl.h>
28#include <errno.h>
29#include <sys/types.h>
30#include <sys/fcntl.h>
31#include <sys/stat.h>
32#include <sys/mman.h>
33#include <sys/param.h>
34#include <mach/mach.h>
35#include <mach/thread_status.h>
36#include <mach-o/loader.h>
37
38#include "ImageLoaderMachOCompressed.h"
39#include "mach-o/dyld_images.h"
40
41#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
42	#define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE			0x02
43#endif
44
45// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables
46#if __LP64__
47	#define RELOC_SIZE 3
48	#define LC_SEGMENT_COMMAND		LC_SEGMENT_64
49	#define LC_ROUTINES_COMMAND		LC_ROUTINES_64
50	struct macho_segment_command	: public segment_command_64  {};
51	struct macho_section			: public section_64  {};
52	struct macho_routines_command	: public routines_command_64  {};
53#else
54	#define RELOC_SIZE 2
55	#define LC_SEGMENT_COMMAND		LC_SEGMENT
56	#define LC_ROUTINES_COMMAND		LC_ROUTINES
57	struct macho_segment_command	: public segment_command {};
58	struct macho_section			: public section  {};
59	struct macho_routines_command	: public routines_command  {};
60#endif
61
62
63static uintptr_t read_uleb128(const uint8_t*& p, const uint8_t* end)
64{
65	uint64_t result = 0;
66	int		 bit = 0;
67	do {
68		if (p == end)
69			dyld::throwf("malformed uleb128");
70
71		uint64_t slice = *p & 0x7f;
72
73		if (bit > 63)
74			dyld::throwf("uleb128 too big for uint64, bit=%d, result=0x%0llX", bit, result);
75		else {
76			result |= (slice << bit);
77			bit += 7;
78		}
79	} while (*p++ & 0x80);
80	return result;
81}
82
83
84static intptr_t read_sleb128(const uint8_t*& p, const uint8_t* end)
85{
86	int64_t result = 0;
87	int bit = 0;
88	uint8_t byte;
89	do {
90		if (p == end)
91			throw "malformed sleb128";
92		byte = *p++;
93		result |= (((int64_t)(byte & 0x7f)) << bit);
94		bit += 7;
95	} while (byte & 0x80);
96	// sign extend negative numbers
97	if ( (byte & 0x40) != 0 )
98		result |= (-1LL) << bit;
99	return result;
100}
101
102
103// create image for main executable
104ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path,
105																		unsigned int segCount, unsigned int libCount, const LinkContext& context)
106{
107	ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, path, segCount, libCount);
108
109	// set slide for PIE programs
110	image->setSlide(slide);
111
112	// for PIE record end of program, to know where to start loading dylibs
113	if ( slide != 0 )
114		fgNextPIEDylibAddress = (uintptr_t)image->getEnd();
115
116	image->instantiateFinish(context);
117	image->setMapped(context);
118
119	if ( context.verboseMapping ) {
120		dyld::log("dyld: Main executable mapped %s\n", path);
121		for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) {
122			const char* name = image->segName(i);
123			if ( (strcmp(name, "__PAGEZERO") == 0) || (strcmp(name, "__UNIXSTACK") == 0)  )
124				dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segPreferredLoadAddress(i), image->segPreferredLoadAddress(i)+image->segSize(i));
125			else
126				dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segActualLoadAddress(i), image->segActualEndAddress(i));
127		}
128	}
129
130	return image;
131}
132
133// create image by mapping in a mach-o file
134ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(const char* path, int fd, const uint8_t* fileData,
135															uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info,
136															unsigned int segCount, unsigned int libCount,
137															const struct linkedit_data_command* codeSigCmd, const LinkContext& context)
138{
139	ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart((macho_header*)fileData, path, segCount, libCount);
140
141	try {
142		// record info about file
143		image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime);
144
145		// if this image is code signed, let kernel validate signature before mapping any pages from image
146		image->loadCodeSignature(codeSigCmd, fd, offsetInFat, context);
147
148		// mmap segments
149		image->mapSegments(fd, offsetInFat, lenInFat, info.st_size, context);
150
151		// probe to see if code signed correctly
152		image->crashIfInvalidCodeSignature();
153
154		// finish construction
155		image->instantiateFinish(context);
156
157		// if path happens to be same as in LC_DYLIB_ID load command use that, otherwise malloc a copy of the path
158		const char* installName = image->getInstallPath();
159		if ( (installName != NULL) && (strcmp(installName, path) == 0) && (path[0] == '/') )
160			image->setPathUnowned(installName);
161#if __MAC_OS_X_VERSION_MIN_REQUIRED
162		// <rdar://problem/6563887> app crashes when libSystem cannot be found
163		else if ( (installName != NULL) && (strcmp(path, "/usr/lib/libgcc_s.1.dylib") == 0) && (strcmp(installName, "/usr/lib/libSystem.B.dylib") == 0) )
164			image->setPathUnowned("/usr/lib/libSystem.B.dylib");
165#endif
166		else if ( (path[0] != '/') || (strstr(path, "../") != NULL) ) {
167			// rdar://problem/10733082 Fix up @rpath based paths during introspection
168			// rdar://problem/5135363 turn relative paths into absolute paths so gdb, Symbolication can later find them
169			char realPath[MAXPATHLEN];
170			if ( fcntl(fd, F_GETPATH, realPath) == 0 )
171				image->setPaths(path, realPath);
172			else
173				image->setPath(path);
174		}
175		else
176			image->setPath(path);
177
178		// make sure path is stable before recording in dyld_all_image_infos
179		image->setMapped(context);
180
181		// pre-fetch content of __DATA and __LINKEDIT segment for faster launches
182		// don't do this on prebound images or if prefetching is disabled
183        if ( !context.preFetchDisabled && !image->isPrebindable()) {
184			image->preFetchDATA(fd, offsetInFat, context);
185			image->markSequentialLINKEDIT(context);
186		}
187	}
188	catch (...) {
189		// ImageLoader::setMapped() can throw an exception to block loading of image
190		// <rdar://problem/6169686> Leaked fSegmentsArray and image segments during failed dlopen_preflight
191		delete image;
192		throw;
193	}
194
195	return image;
196}
197
198// create image by using cached mach-o file
199ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromCache(const macho_header* mh, const char* path, long slide,
200																		const struct stat& info, unsigned int segCount,
201																		unsigned int libCount, const LinkContext& context)
202{
203	ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, path, segCount, libCount);
204	try {
205		// record info about file
206		image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime);
207
208		// remember this is from shared cache and cannot be unloaded
209		image->fInSharedCache = true;
210		image->setNeverUnload();
211		image->setSlide(slide);
212
213		// segments already mapped in cache
214		if ( context.verboseMapping ) {
215			dyld::log("dyld: Using shared cached for %s\n", path);
216			for(unsigned int i=0; i < image->fSegmentsCount; ++i) {
217				dyld::log("%18s at 0x%08lX->0x%08lX\n", image->segName(i), image->segActualLoadAddress(i), image->segActualEndAddress(i));
218			}
219		}
220
221		image->instantiateFinish(context);
222		image->setMapped(context);
223	}
224	catch (...) {
225		// ImageLoader::setMapped() can throw an exception to block loading of image
226		// <rdar://problem/6169686> Leaked fSegmentsArray and image segments during failed dlopen_preflight
227		delete image;
228		throw;
229	}
230
231	return image;
232}
233
234// create image by copying an in-memory mach-o file
235ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len,
236															unsigned int segCount, unsigned int libCount, const LinkContext& context)
237{
238	ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, moduleName, segCount, libCount);
239	try {
240		// map segments
241		if ( mh->filetype == MH_EXECUTE )
242			throw "can't load another MH_EXECUTE";
243
244		// vmcopy segments
245		image->mapSegments((const void*)mh, len, context);
246
247		// for compatibility, never unload dylibs loaded from memory
248		image->setNeverUnload();
249
250		// bundle loads need path copied
251		if ( moduleName != NULL )
252			image->setPath(moduleName);
253
254		image->instantiateFinish(context);
255		image->setMapped(context);
256	}
257	catch (...) {
258		// ImageLoader::setMapped() can throw an exception to block loading of image
259		// <rdar://problem/6169686> Leaked fSegmentsArray and image segments during failed dlopen_preflight
260		delete image;
261		throw;
262	}
263
264	return image;
265}
266
267
268ImageLoaderMachOCompressed::ImageLoaderMachOCompressed(const macho_header* mh, const char* path, unsigned int segCount,
269																		uint32_t segOffsets[], unsigned int libCount)
270 : ImageLoaderMachO(mh, path, segCount, segOffsets, libCount), fDyldInfo(NULL)
271{
272}
273
274ImageLoaderMachOCompressed::~ImageLoaderMachOCompressed()
275{
276	// don't do clean up in ~ImageLoaderMachO() because virtual call to segmentCommandOffsets() won't work
277	destroy();
278}
279
280
281
282// construct ImageLoaderMachOCompressed using "placement new" with SegmentMachO objects array at end
283ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateStart(const macho_header* mh, const char* path,
284																			unsigned int segCount, unsigned int libCount)
285{
286	size_t size = sizeof(ImageLoaderMachOCompressed) + segCount * sizeof(uint32_t) + libCount * sizeof(ImageLoader*);
287	ImageLoaderMachOCompressed* allocatedSpace = static_cast<ImageLoaderMachOCompressed*>(malloc(size));
288	if ( allocatedSpace == NULL )
289		throw "malloc failed";
290	uint32_t* segOffsets = ((uint32_t*)(((uint8_t*)allocatedSpace) + sizeof(ImageLoaderMachOCompressed)));
291	bzero(&segOffsets[segCount], libCount*sizeof(void*));	// zero out lib array
292	return new (allocatedSpace) ImageLoaderMachOCompressed(mh, path, segCount, segOffsets, libCount);
293}
294
295
296// common code to finish initializing object
297void ImageLoaderMachOCompressed::instantiateFinish(const LinkContext& context)
298{
299	// now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide
300	this->parseLoadCmds();
301}
302
303uint32_t* ImageLoaderMachOCompressed::segmentCommandOffsets() const
304{
305	return ((uint32_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed)));
306}
307
308
309ImageLoader* ImageLoaderMachOCompressed::libImage(unsigned int libIndex) const
310{
311	const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t)));
312	// mask off low bits
313	return (ImageLoader*)(images[libIndex] & (-4));
314}
315
316bool ImageLoaderMachOCompressed::libReExported(unsigned int libIndex) const
317{
318	const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t)));
319	// re-export flag is low bit
320	return ((images[libIndex] & 1) != 0);
321}
322
323bool ImageLoaderMachOCompressed::libIsUpward(unsigned int libIndex) const
324{
325	const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t)));
326	// re-export flag is second bit
327	return ((images[libIndex] & 2) != 0);
328}
329
330
331void ImageLoaderMachOCompressed::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported, bool upward)
332{
333	uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t)));
334	uintptr_t value = (uintptr_t)image;
335	if ( reExported )
336		value |= 1;
337	if ( upward )
338		value |= 2;
339	images[libIndex] = value;
340}
341
342
343void ImageLoaderMachOCompressed::markFreeLINKEDIT(const LinkContext& context)
344{
345	// mark that we are done with rebase and bind info
346	markLINKEDIT(context, MADV_FREE);
347}
348
349void ImageLoaderMachOCompressed::markSequentialLINKEDIT(const LinkContext& context)
350{
351	// mark the rebase and bind info and using sequential access
352	markLINKEDIT(context, MADV_SEQUENTIAL);
353}
354
355void ImageLoaderMachOCompressed::markLINKEDIT(const LinkContext& context, int advise)
356{
357	// if not loaded at preferred address, mark rebase info
358	uintptr_t start = 0;
359	if ( (fSlide != 0) && (fDyldInfo->rebase_size != 0) )
360		start = (uintptr_t)fLinkEditBase + fDyldInfo->rebase_off;
361	else if ( fDyldInfo->bind_off != 0 )
362		start = (uintptr_t)fLinkEditBase + fDyldInfo->bind_off;
363	else
364		return; // no binding info to prefetch
365
366	// end is at end of bind info
367	uintptr_t end = 0;
368	if ( fDyldInfo->bind_off != 0 )
369		end = (uintptr_t)fLinkEditBase + fDyldInfo->bind_off + fDyldInfo->bind_size;
370	else if ( fDyldInfo->rebase_off != 0 )
371		end = (uintptr_t)fLinkEditBase + fDyldInfo->rebase_off + fDyldInfo->rebase_size;
372	else
373		return;
374
375
376	// round to whole pages
377	start = start & (-4096);
378	end = (end + 4095) & (-4096);
379
380	// do nothing if only one page of rebase/bind info
381	if ( (end-start) <= 4096 )
382		return;
383
384	// tell kernel about our access to these pages
385	madvise((void*)start, end-start, advise);
386	if ( context.verboseMapping ) {
387		const char* adstr = "sequential";
388		if ( advise == MADV_FREE )
389			adstr = "free";
390		dyld::log("%18s %s 0x%0lX -> 0x%0lX for %s\n", "__LINKEDIT", adstr, start, end-1, this->getPath());
391	}
392}
393
394
395
396void ImageLoaderMachOCompressed::rebaseAt(const LinkContext& context, uintptr_t addr, uintptr_t slide, uint8_t type)
397{
398	if ( context.verboseRebase ) {
399		dyld::log("dyld: rebase: %s:*0x%08lX += 0x%08lX\n", this->getShortName(), (uintptr_t)addr, slide);
400	}
401	//dyld::log("0x%08lX type=%d\n", addr, type);
402	uintptr_t* locationToFix = (uintptr_t*)addr;
403	switch (type) {
404		case REBASE_TYPE_POINTER:
405			*locationToFix += slide;
406			break;
407		case REBASE_TYPE_TEXT_ABSOLUTE32:
408			*locationToFix += slide;
409			break;
410		default:
411			dyld::throwf("bad rebase type %d", type);
412	}
413}
414
415void ImageLoaderMachOCompressed::throwBadRebaseAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex,
416										const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos)
417{
418	dyld::throwf("malformed rebase opcodes (%ld/%ld): address 0x%08lX is beyond end of segment %s (0x%08lX -> 0x%08lX)",
419		(intptr_t)(pos-startOpcodes), (intptr_t)(endOpcodes-startOpcodes), address, segName(segmentIndex),
420		segActualLoadAddress(segmentIndex), segmentEndAddress);
421}
422
423void ImageLoaderMachOCompressed::rebase(const LinkContext& context)
424{
425	CRSetCrashLogMessage2(this->getPath());
426	const uintptr_t slide = this->fSlide;
427	const uint8_t* const start = fLinkEditBase + fDyldInfo->rebase_off;
428	const uint8_t* const end = &start[fDyldInfo->rebase_size];
429	const uint8_t* p = start;
430
431	try {
432		uint8_t type = 0;
433		int segmentIndex = 0;
434		uintptr_t address = segActualLoadAddress(0);
435		uintptr_t segmentEndAddress = segActualEndAddress(0);
436		uint32_t count;
437		uint32_t skip;
438		bool done = false;
439		while ( !done && (p < end) ) {
440			uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
441			uint8_t opcode = *p & REBASE_OPCODE_MASK;
442			++p;
443			switch (opcode) {
444				case REBASE_OPCODE_DONE:
445					done = true;
446					break;
447				case REBASE_OPCODE_SET_TYPE_IMM:
448					type = immediate;
449					break;
450				case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
451					segmentIndex = immediate;
452					if ( segmentIndex > fSegmentsCount )
453						dyld::throwf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n",
454								segmentIndex, fSegmentsCount);
455					address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end);
456					segmentEndAddress = segActualEndAddress(segmentIndex);
457					break;
458				case REBASE_OPCODE_ADD_ADDR_ULEB:
459					address += read_uleb128(p, end);
460					break;
461				case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
462					address += immediate*sizeof(uintptr_t);
463					break;
464				case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
465					for (int i=0; i < immediate; ++i) {
466						if ( address >= segmentEndAddress )
467							throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p);
468						rebaseAt(context, address, slide, type);
469						address += sizeof(uintptr_t);
470					}
471					fgTotalRebaseFixups += immediate;
472					break;
473				case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
474					count = read_uleb128(p, end);
475					for (uint32_t i=0; i < count; ++i) {
476						if ( address >= segmentEndAddress )
477							throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p);
478						rebaseAt(context, address, slide, type);
479						address += sizeof(uintptr_t);
480					}
481					fgTotalRebaseFixups += count;
482					break;
483				case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
484					if ( address >= segmentEndAddress )
485						throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p);
486					rebaseAt(context, address, slide, type);
487					address += read_uleb128(p, end) + sizeof(uintptr_t);
488					++fgTotalRebaseFixups;
489					break;
490				case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
491					count = read_uleb128(p, end);
492					skip = read_uleb128(p, end);
493					for (uint32_t i=0; i < count; ++i) {
494						if ( address >= segmentEndAddress )
495							throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p);
496						rebaseAt(context, address, slide, type);
497						address += skip + sizeof(uintptr_t);
498					}
499					fgTotalRebaseFixups += count;
500					break;
501				default:
502					dyld::throwf("bad rebase opcode %d", *p);
503			}
504		}
505	}
506	catch (const char* msg) {
507		const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath());
508		free((void*)msg);
509		throw newMsg;
510	}
511	CRSetCrashLogMessage2(NULL);
512}
513
514//
515// This function is the hotspot of symbol lookup.  It was pulled out of findExportedSymbol()
516// to enable it to be re-written in assembler if needed.
517//
518const uint8_t* ImageLoaderMachOCompressed::trieWalk(const uint8_t* start, const uint8_t* end, const char* s)
519{
520	const uint8_t* p = start;
521	while ( p != NULL ) {
522		uint32_t terminalSize = *p++;
523		if ( terminalSize > 127 ) {
524			// except for re-export-with-rename, all terminal sizes fit in one byte
525			--p;
526			terminalSize = read_uleb128(p, end);
527		}
528		if ( (*s == '\0') && (terminalSize != 0) ) {
529			//dyld::log("trieWalk(%p) returning %p\n", start, p);
530			return p;
531		}
532		const uint8_t* children = p + terminalSize;
533		//dyld::log("trieWalk(%p) sym=%s, terminalSize=%d, children=%p\n", start, s, terminalSize, children);
534		uint8_t childrenRemaining = *children++;
535		p = children;
536		uint32_t nodeOffset = 0;
537		for (; childrenRemaining > 0; --childrenRemaining) {
538			const char* ss = s;
539			//dyld::log("trieWalk(%p) child str=%s\n", start, (char*)p);
540			bool wrongEdge = false;
541			// scan whole edge to get to next edge
542			// if edge is longer than target symbol name, don't read past end of symbol name
543			char c = *p;
544			while ( c != '\0' ) {
545				if ( !wrongEdge ) {
546					if ( c != *ss )
547						wrongEdge = true;
548					++ss;
549				}
550				++p;
551				c = *p;
552			}
553			if ( wrongEdge ) {
554				// advance to next child
555				++p; // skip over zero terminator
556				// skip over uleb128 until last byte is found
557				while ( (*p & 0x80) != 0 )
558					++p;
559				++p; // skil over last byte of uleb128
560			}
561			else {
562 				// the symbol so far matches this edge (child)
563				// so advance to the child's node
564				++p;
565				nodeOffset = read_uleb128(p, end);
566				s = ss;
567				//dyld::log("trieWalk() found matching edge advancing to node 0x%x\n", nodeOffset);
568				break;
569			}
570		}
571		if ( nodeOffset != 0 )
572			p = &start[nodeOffset];
573		else
574			p = NULL;
575	}
576	//dyld::log("trieWalk(%p) return NULL\n", start);
577	return NULL;
578}
579
580
581const ImageLoader::Symbol* ImageLoaderMachOCompressed::findExportedSymbol(const char* symbol, const ImageLoader** foundIn) const
582{
583	//dyld::log("Compressed::findExportedSymbol(%s) in %s\n", symbol, this->getShortName());
584	if ( fDyldInfo->export_size == 0 )
585		return NULL;
586#if LOG_BINDINGS
587	dyld::logBindings("%s: %s\n", this->getShortName(), symbol);
588#endif
589	++ImageLoaderMachO::fgSymbolTrieSearchs;
590	const uint8_t* start = &fLinkEditBase[fDyldInfo->export_off];
591	const uint8_t* end = &start[fDyldInfo->export_size];
592	const uint8_t* foundNodeStart = this->trieWalk(start, end, symbol);
593	if ( foundNodeStart != NULL ) {
594		const uint8_t* p = foundNodeStart;
595		const uint32_t flags = read_uleb128(p, end);
596		// found match, return pointer to terminal part of node
597		if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
598			// re-export from another dylib, lookup there
599			const uint32_t ordinal = read_uleb128(p, end);
600			const char* importedName = (char*)p;
601			if ( importedName[0] == '\0' )
602				importedName = symbol;
603			if ( (ordinal > 0) && (ordinal <= libraryCount()) ) {
604				const ImageLoader* reexportedFrom = libImage(ordinal-1);
605				//dyld::log("Compressed::findExportedSymbol(), %s -> %s/%s\n", symbol, reexportedFrom->getShortName(), importedName);
606				return reexportedFrom->findExportedSymbol(importedName, true, foundIn);
607			}
608			else {
609				//dyld::throwf("bad mach-o binary, library ordinal (%u) invalid (max %u) for re-exported symbol %s in %s",
610				//	ordinal, libraryCount(), symbol, this->getPath());
611			}
612		}
613		else {
614			//dyld::log("findExportedSymbol(%s) in %s found match, returning %p\n", symbol, this->getShortName(), p);
615			if ( foundIn != NULL )
616				*foundIn = (ImageLoader*)this;
617			// return pointer to terminal part of node
618			return (Symbol*)foundNodeStart;
619		}
620	}
621	return NULL;
622}
623
624
625bool ImageLoaderMachOCompressed::containsSymbol(const void* addr) const
626{
627	const uint8_t* start = &fLinkEditBase[fDyldInfo->export_off];
628	const uint8_t* end = &start[fDyldInfo->export_size];
629	return ( (start <= addr) && (addr < end) );
630}
631
632
633uintptr_t ImageLoaderMachOCompressed::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const
634{
635	const uint8_t* exportNode = (uint8_t*)symbol;
636	const uint8_t* exportTrieStart = fLinkEditBase + fDyldInfo->export_off;
637	const uint8_t* exportTrieEnd = exportTrieStart + fDyldInfo->export_size;
638	if ( (exportNode < exportTrieStart) || (exportNode > exportTrieEnd) )
639		throw "symbol is not in trie";
640	//dyld::log("exportedSymbolAddress(): node=%p, nodeOffset=0x%04X in %s\n", symbol, (int)((uint8_t*)symbol - exportTrieStart), this->getShortName());
641	uint32_t flags = read_uleb128(exportNode, exportTrieEnd);
642	switch ( flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) {
643		case EXPORT_SYMBOL_FLAGS_KIND_REGULAR:
644			if ( runResolver && (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) {
645				// this node has a stub and resolver, run the resolver to get target address
646				uintptr_t stub = read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; // skip over stub
647				// <rdar://problem/10657737> interposing dylibs have the stub address as their replacee
648				for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) {
649					// replace all references to 'replacee' with 'replacement'
650					if ( (stub == it->replacee) && (requestor != it->replacementImage) ) {
651						if ( context.verboseInterposing ) {
652							dyld::log("dyld interposing: lazy replace 0x%lX with 0x%lX from %s\n",
653									  it->replacee, it->replacement, this->getPath());
654						}
655						return it->replacement;
656					}
657				}
658				typedef uintptr_t (*ResolverProc)(void);
659				ResolverProc resolver = (ResolverProc)(read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData);
660				uintptr_t result = (*resolver)();
661				if ( context.verboseBind )
662					dyld::log("dyld: resolver at %p returned 0x%08lX\n", resolver, result);
663				return result;
664			}
665			return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData;
666		case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL:
667			if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER )
668				dyld::throwf("unsupported exported symbol kind. flags=%d at node=%p", flags, symbol);
669			return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData;
670		case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE:
671			if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER )
672				dyld::throwf("unsupported exported symbol kind. flags=%d at node=%p", flags, symbol);
673			return read_uleb128(exportNode, exportTrieEnd);
674		default:
675			dyld::throwf("unsupported exported symbol kind. flags=%d at node=%p", flags, symbol);
676	}
677}
678
679bool ImageLoaderMachOCompressed::exportedSymbolIsWeakDefintion(const Symbol* symbol) const
680{
681	const uint8_t* exportNode = (uint8_t*)symbol;
682	const uint8_t* exportTrieStart = fLinkEditBase + fDyldInfo->export_off;
683	const uint8_t* exportTrieEnd = exportTrieStart + fDyldInfo->export_size;
684	if ( (exportNode < exportTrieStart) || (exportNode > exportTrieEnd) )
685		throw "symbol is not in trie";
686	uint32_t flags = read_uleb128(exportNode, exportTrieEnd);
687	return ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION );
688}
689
690
691const char* ImageLoaderMachOCompressed::exportedSymbolName(const Symbol* symbol) const
692{
693	throw "NSNameOfSymbol() not supported with compressed LINKEDIT";
694}
695
696unsigned int ImageLoaderMachOCompressed::exportedSymbolCount() const
697{
698	throw "NSSymbolDefinitionCountInObjectFileImage() not supported with compressed LINKEDIT";
699}
700
701const ImageLoader::Symbol* ImageLoaderMachOCompressed::exportedSymbolIndexed(unsigned int index) const
702{
703	throw "NSSymbolDefinitionNameInObjectFileImage() not supported with compressed LINKEDIT";
704}
705
706unsigned int ImageLoaderMachOCompressed::importedSymbolCount() const
707{
708	throw "NSSymbolReferenceCountInObjectFileImage() not supported with compressed LINKEDIT";
709}
710
711const ImageLoader::Symbol* ImageLoaderMachOCompressed::importedSymbolIndexed(unsigned int index) const
712{
713	throw "NSSymbolReferenceCountInObjectFileImage() not supported with compressed LINKEDIT";
714}
715
716const char* ImageLoaderMachOCompressed::importedSymbolName(const Symbol* symbol) const
717{
718	throw "NSSymbolReferenceNameInObjectFileImage() not supported with compressed LINKEDIT";
719}
720
721
722
723uintptr_t ImageLoaderMachOCompressed::resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import,
724													bool runResolver, const ImageLoader** foundIn)
725{
726	const Symbol* sym;
727	if ( context.flatExportFinder(symbolName, &sym, foundIn) ) {
728		if ( *foundIn != this )
729			context.addDynamicReference(this, const_cast<ImageLoader*>(*foundIn));
730		return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver);
731	}
732	// if a bundle is loaded privately the above will not find its exports
733	if ( this->isBundle() && this->hasHiddenExports() ) {
734		// look in self for needed symbol
735		sym = this->ImageLoaderMachO::findExportedSymbol(symbolName, false, foundIn);
736		if ( sym != NULL )
737			return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver);
738	}
739	if ( weak_import ) {
740		// definition can't be found anywhere, ok because it is weak, just return 0
741		return 0;
742	}
743	throwSymbolNotFound(context, symbolName, this->getPath(), "flat namespace");
744}
745
746
747uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context, const ImageLoader* targetImage, bool weak_import,
748												const char* symbolName, bool runResolver, const ImageLoader** foundIn)
749{
750	// two level lookup
751	const Symbol* sym = targetImage->findExportedSymbol(symbolName, true, foundIn);
752	if ( sym != NULL ) {
753		return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver);
754	}
755
756	if ( weak_import ) {
757		// definition can't be found anywhere, ok because it is weak, just return 0
758		return 0;
759	}
760
761	// nowhere to be found
762	throwSymbolNotFound(context, symbolName, this->getPath(), targetImage->getPath());
763}
764
765
766uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const char* symbolName,
767													uint8_t symboFlags, int libraryOrdinal, const ImageLoader** targetImage,
768													LastLookup* last, bool runResolver)
769{
770	*targetImage = NULL;
771
772	// only clients that benefit from caching last lookup pass in a LastLookup struct
773	if ( last != NULL ) {
774		if ( (last->ordinal == libraryOrdinal)
775			&& (last->flags == symboFlags)
776			&& (last->name == symbolName) ) {
777				*targetImage = last->foundIn;
778				return last->result;
779			}
780	}
781
782	bool weak_import = (symboFlags & BIND_SYMBOL_FLAGS_WEAK_IMPORT);
783	uintptr_t symbolAddress;
784	if ( context.bindFlat || (libraryOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP) ) {
785		symbolAddress = this->resolveFlat(context, symbolName, weak_import, runResolver, targetImage);
786	}
787	else {
788		if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) {
789			*targetImage = context.mainExecutable;
790		}
791		else if ( libraryOrdinal == BIND_SPECIAL_DYLIB_SELF ) {
792			*targetImage = this;
793		}
794		else if ( libraryOrdinal <= 0 ) {
795			dyld::throwf("bad mach-o binary, unknown special library ordinal (%u) too big for symbol %s in %s",
796				libraryOrdinal, symbolName, this->getPath());
797		}
798		else if ( (unsigned)libraryOrdinal <= libraryCount() ) {
799			*targetImage = libImage(libraryOrdinal-1);
800		}
801		else {
802			dyld::throwf("bad mach-o binary, library ordinal (%u) too big (max %u) for symbol %s in %s",
803				libraryOrdinal, libraryCount(), symbolName, this->getPath());
804		}
805		if ( *targetImage == NULL ) {
806			if ( weak_import ) {
807				// if target library not loaded and reference is weak or library is weak return 0
808				symbolAddress = 0;
809			}
810			else {
811				dyld::throwf("can't resolve symbol %s in %s because dependent dylib #%d could not be loaded",
812					symbolName, this->getPath(), libraryOrdinal);
813			}
814		}
815		else {
816			symbolAddress = resolveTwolevel(context, *targetImage, weak_import, symbolName, runResolver, targetImage);
817		}
818	}
819
820	// save off lookup results if client wants
821	if ( last != NULL ) {
822		last->ordinal	= libraryOrdinal;
823		last->flags		= symboFlags;
824		last->name		= symbolName;
825		last->foundIn	= *targetImage;
826		last->result	= symbolAddress;
827	}
828
829	return symbolAddress;
830}
831
832uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName,
833								uint8_t symboFlags, intptr_t addend, int libraryOrdinal, const char* msg,
834								LastLookup* last, bool runResolver)
835{
836	const ImageLoader*	targetImage;
837	uintptr_t			symbolAddress;
838
839	// resolve symbol
840	symbolAddress = this->resolve(context, symbolName, symboFlags, libraryOrdinal, &targetImage, last, runResolver);
841
842	// do actual update
843	return this->bindLocation(context, addr, symbolAddress, targetImage, type, symbolName, addend, msg);
844}
845
846void ImageLoaderMachOCompressed::throwBadBindingAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex,
847										const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos)
848{
849	dyld::throwf("malformed binding opcodes (%ld/%ld): address 0x%08lX is beyond end of segment %s (0x%08lX -> 0x%08lX)",
850		(intptr_t)(pos-startOpcodes), (intptr_t)(endOpcodes-startOpcodes), address, segName(segmentIndex),
851		segActualLoadAddress(segmentIndex), segmentEndAddress);
852}
853
854
855void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLazysBound)
856{
857	CRSetCrashLogMessage2(this->getPath());
858
859	// if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind
860	// note: flat-namespace binaries need to have imports rebound (even if correctly prebound)
861	if ( this->usablePrebinding(context) ) {
862		// don't need to bind
863	}
864	else {
865
866	#if TEXT_RELOC_SUPPORT
867		// if there are __TEXT fixups, temporarily make __TEXT writable
868		if ( fTextSegmentBinds )
869			this->makeTextSegmentWritable(context, true);
870	#endif
871
872		// run through all binding opcodes
873		eachBind(context, &ImageLoaderMachOCompressed::bindAt);
874
875	#if TEXT_RELOC_SUPPORT
876		// if there were __TEXT fixups, restore write protection
877		if ( fTextSegmentBinds )
878			this->makeTextSegmentWritable(context, false);
879	#endif
880
881		// if this image is in the shared cache, but depends on something no longer in the shared cache,
882		// there is no way to reset the lazy pointers, so force bind them now
883		if ( forceLazysBound || fInSharedCache )
884			this->doBindJustLazies(context);
885
886		// this image is in cache, but something below it is not.  If
887        // this image has lazy pointer to a resolver function, then
888        // the stub may have been altered to point to a shared lazy pointer.
889		if ( fInSharedCache )
890			this->updateOptimizedLazyPointers(context);
891
892		// tell kernel we are done with chunks of LINKEDIT
893		if ( !context.preFetchDisabled )
894			this->markFreeLINKEDIT(context);
895	}
896
897	// set up dyld entry points in image
898	// do last so flat main executables will have __dyld or __program_vars set up
899	this->setupLazyPointerHandler(context);
900	CRSetCrashLogMessage2(NULL);
901}
902
903
904void ImageLoaderMachOCompressed::doBindJustLazies(const LinkContext& context)
905{
906	eachLazyBind(context, &ImageLoaderMachOCompressed::bindAt);
907}
908
909void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handler handler)
910{
911	try {
912		uint8_t type = 0;
913		int segmentIndex = 0;
914		uintptr_t address = segActualLoadAddress(0);
915		uintptr_t segmentEndAddress = segActualEndAddress(0);
916		const char* symbolName = NULL;
917		uint8_t symboFlags = 0;
918		int libraryOrdinal = 0;
919		intptr_t addend = 0;
920		uint32_t count;
921		uint32_t skip;
922		LastLookup last = { 0, 0, NULL, 0, NULL };
923		const uint8_t* const start = fLinkEditBase + fDyldInfo->bind_off;
924		const uint8_t* const end = &start[fDyldInfo->bind_size];
925		const uint8_t* p = start;
926		bool done = false;
927		while ( !done && (p < end) ) {
928			uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
929			uint8_t opcode = *p & BIND_OPCODE_MASK;
930			++p;
931			switch (opcode) {
932				case BIND_OPCODE_DONE:
933					done = true;
934					break;
935				case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
936					libraryOrdinal = immediate;
937					break;
938				case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
939					libraryOrdinal = read_uleb128(p, end);
940					break;
941				case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
942					// the special ordinals are negative numbers
943					if ( immediate == 0 )
944						libraryOrdinal = 0;
945					else {
946						int8_t signExtended = BIND_OPCODE_MASK | immediate;
947						libraryOrdinal = signExtended;
948					}
949					break;
950				case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
951					symbolName = (char*)p;
952					symboFlags = immediate;
953					while (*p != '\0')
954						++p;
955					++p;
956					break;
957				case BIND_OPCODE_SET_TYPE_IMM:
958					type = immediate;
959					break;
960				case BIND_OPCODE_SET_ADDEND_SLEB:
961					addend = read_sleb128(p, end);
962					break;
963				case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
964					segmentIndex = immediate;
965					if ( segmentIndex > fSegmentsCount )
966						dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n",
967								segmentIndex, fSegmentsCount);
968					address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end);
969					segmentEndAddress = segActualEndAddress(segmentIndex);
970					break;
971				case BIND_OPCODE_ADD_ADDR_ULEB:
972					address += read_uleb128(p, end);
973					break;
974				case BIND_OPCODE_DO_BIND:
975					if ( address >= segmentEndAddress )
976						throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
977					(this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
978					address += sizeof(intptr_t);
979					break;
980				case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
981					if ( address >= segmentEndAddress )
982						throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
983					(this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
984					address += read_uleb128(p, end) + sizeof(intptr_t);
985					break;
986				case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
987					if ( address >= segmentEndAddress )
988						throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
989					(this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
990					address += immediate*sizeof(intptr_t) + sizeof(intptr_t);
991					break;
992				case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
993					count = read_uleb128(p, end);
994					skip = read_uleb128(p, end);
995					for (uint32_t i=0; i < count; ++i) {
996						if ( address >= segmentEndAddress )
997							throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
998						(this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
999						address += skip + sizeof(intptr_t);
1000					}
1001					break;
1002				default:
1003					dyld::throwf("bad bind opcode %d in bind info", *p);
1004			}
1005		}
1006	}
1007	catch (const char* msg) {
1008		const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath());
1009		free((void*)msg);
1010		throw newMsg;
1011	}
1012}
1013
1014void ImageLoaderMachOCompressed::eachLazyBind(const LinkContext& context, bind_handler handler)
1015{
1016	try {
1017		uint8_t type = BIND_TYPE_POINTER;
1018		int segmentIndex = 0;
1019		uintptr_t address = segActualLoadAddress(0);
1020		uintptr_t segmentEndAddress = segActualEndAddress(0);
1021		const char* symbolName = NULL;
1022		uint8_t symboFlags = 0;
1023		int libraryOrdinal = 0;
1024		intptr_t addend = 0;
1025		const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off;
1026		const uint8_t* const end = &start[fDyldInfo->lazy_bind_size];
1027		const uint8_t* p = start;
1028		bool done = false;
1029		while ( !done && (p < end) ) {
1030			uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
1031			uint8_t opcode = *p & BIND_OPCODE_MASK;
1032			++p;
1033			switch (opcode) {
1034				case BIND_OPCODE_DONE:
1035					// there is BIND_OPCODE_DONE at end of each lazy bind, don't stop until end of whole sequence
1036					break;
1037				case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
1038					libraryOrdinal = immediate;
1039					break;
1040				case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
1041					libraryOrdinal = read_uleb128(p, end);
1042					break;
1043				case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
1044					// the special ordinals are negative numbers
1045					if ( immediate == 0 )
1046						libraryOrdinal = 0;
1047					else {
1048						int8_t signExtended = BIND_OPCODE_MASK | immediate;
1049						libraryOrdinal = signExtended;
1050					}
1051					break;
1052				case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
1053					symbolName = (char*)p;
1054					symboFlags = immediate;
1055					while (*p != '\0')
1056						++p;
1057					++p;
1058					break;
1059				case BIND_OPCODE_SET_TYPE_IMM:
1060					type = immediate;
1061					break;
1062				case BIND_OPCODE_SET_ADDEND_SLEB:
1063					addend = read_sleb128(p, end);
1064					break;
1065				case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
1066					segmentIndex = immediate;
1067					if ( segmentIndex > fSegmentsCount )
1068						dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n",
1069								segmentIndex, fSegmentsCount);
1070					address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end);
1071					segmentEndAddress = segActualEndAddress(segmentIndex);
1072					break;
1073				case BIND_OPCODE_ADD_ADDR_ULEB:
1074					address += read_uleb128(p, end);
1075					break;
1076				case BIND_OPCODE_DO_BIND:
1077					if ( address >= segmentEndAddress )
1078						throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
1079					(this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "forced lazy ", NULL, false);
1080					address += sizeof(intptr_t);
1081					break;
1082				case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
1083				case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
1084				case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
1085				default:
1086					dyld::throwf("bad lazy bind opcode %d", *p);
1087			}
1088		}
1089	}
1090
1091	catch (const char* msg) {
1092		const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath());
1093		free((void*)msg);
1094		throw newMsg;
1095	}
1096}
1097
1098// A program built targeting 10.5 will have hybrid stubs.  When used with weak symbols
1099// the classic lazy loader is used even when running on 10.6
1100uintptr_t ImageLoaderMachOCompressed::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context)
1101{
1102	// only works with compressed LINKEDIT if classic symbol table is also present
1103	const macho_nlist* symbolTable = NULL;
1104	const char* symbolTableStrings = NULL;
1105	const dysymtab_command* dynSymbolTable = NULL;
1106	const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
1107	const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
1108	const struct load_command* cmd = cmds;
1109	for (uint32_t i = 0; i < cmd_count; ++i) {
1110		switch (cmd->cmd) {
1111			case LC_SYMTAB:
1112				{
1113					const struct symtab_command* symtab = (struct symtab_command*)cmd;
1114					symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff];
1115					symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]);
1116				}
1117				break;
1118			case LC_DYSYMTAB:
1119				dynSymbolTable = (struct dysymtab_command*)cmd;
1120				break;
1121		}
1122		cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
1123	}
1124	// no symbol table => no lookup by address
1125	if ( (symbolTable == NULL) || (dynSymbolTable == NULL) )
1126		dyld::throwf("classic lazy binding used with compressed LINKEDIT at %p in image %s", lazyPointer, this->getPath());
1127
1128	// scan for all lazy-pointer sections
1129	const bool twoLevel = this->usesTwoLevelNameSpace();
1130	const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff];
1131	cmd = cmds;
1132	for (uint32_t i = 0; i < cmd_count; ++i) {
1133		switch (cmd->cmd) {
1134			case LC_SEGMENT_COMMAND:
1135				{
1136					const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
1137					const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
1138					const struct macho_section* const sectionsEnd = &sectionsStart[seg->nsects];
1139					for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
1140						const uint8_t type = sect->flags & SECTION_TYPE;
1141						uint32_t symbolIndex = INDIRECT_SYMBOL_LOCAL;
1142						if ( type == S_LAZY_SYMBOL_POINTERS ) {
1143							const uint32_t pointerCount = sect->size / sizeof(uintptr_t);
1144							uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide);
1145							if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) {
1146								const uint32_t indirectTableOffset = sect->reserved1;
1147								const uint32_t lazyIndex = lazyPointer - symbolPointers;
1148								symbolIndex = indirectTable[indirectTableOffset + lazyIndex];
1149							}
1150						}
1151						if ( (symbolIndex != INDIRECT_SYMBOL_ABS) && (symbolIndex != INDIRECT_SYMBOL_LOCAL) ) {
1152							const macho_nlist* symbol = &symbolTable[symbolIndex];
1153							const char* symbolName = &symbolTableStrings[symbol->n_un.n_strx];
1154							int libraryOrdinal = GET_LIBRARY_ORDINAL(symbol->n_desc);
1155							if ( !twoLevel || context.bindFlat )
1156								libraryOrdinal = BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
1157							uintptr_t ptrToBind = (uintptr_t)lazyPointer;
1158							uintptr_t symbolAddr = bindAt(context, ptrToBind, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL);
1159							++fgTotalLazyBindFixups;
1160							return symbolAddr;
1161						}
1162					}
1163				}
1164				break;
1165		}
1166		cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
1167	}
1168	dyld::throwf("lazy pointer not found at address %p in image %s", lazyPointer, this->getPath());
1169}
1170
1171
1172uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context,
1173															void (*lock)(), void (*unlock)())
1174{
1175	// <rdar://problem/8663923> race condition with flat-namespace lazy binding
1176	if ( this->usesTwoLevelNameSpace() ) {
1177		// two-level namespace lookup does not require lock because dependents can't be unloaded before this image
1178	}
1179	else {
1180		// acquire dyld global lock
1181		if ( lock != NULL )
1182			lock();
1183	}
1184
1185	const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off;
1186	const uint8_t* const end = &start[fDyldInfo->lazy_bind_size];
1187	if ( lazyBindingInfoOffset > fDyldInfo->lazy_bind_size ) {
1188		dyld::throwf("fast lazy bind offset out of range (%u, max=%u) in image %s",
1189			lazyBindingInfoOffset, fDyldInfo->lazy_bind_size, this->getPath());
1190	}
1191
1192	uint8_t type = BIND_TYPE_POINTER;
1193	uintptr_t address = 0;
1194	const char* symbolName = NULL;
1195	uint8_t symboFlags = 0;
1196	int libraryOrdinal = 0;
1197	bool done = false;
1198	uintptr_t result = 0;
1199	const uint8_t* p = &start[lazyBindingInfoOffset];
1200	while ( !done && (p < end) ) {
1201		uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
1202		uint8_t opcode = *p & BIND_OPCODE_MASK;
1203		++p;
1204		switch (opcode) {
1205			case BIND_OPCODE_DONE:
1206				done = true;
1207				break;
1208			case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
1209				libraryOrdinal = immediate;
1210				break;
1211			case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
1212				libraryOrdinal = read_uleb128(p, end);
1213				break;
1214			case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
1215				// the special ordinals are negative numbers
1216				if ( immediate == 0 )
1217					libraryOrdinal = 0;
1218				else {
1219					int8_t signExtended = BIND_OPCODE_MASK | immediate;
1220					libraryOrdinal = signExtended;
1221				}
1222				break;
1223			case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
1224				symbolName = (char*)p;
1225				symboFlags = immediate;
1226				while (*p != '\0')
1227					++p;
1228				++p;
1229				break;
1230			case BIND_OPCODE_SET_TYPE_IMM:
1231				type = immediate;
1232				break;
1233			case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
1234				if ( immediate > fSegmentsCount )
1235					dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n",
1236							immediate, fSegmentsCount);
1237				address = segActualLoadAddress(immediate) + read_uleb128(p, end);
1238				break;
1239			case BIND_OPCODE_DO_BIND:
1240
1241
1242				result = this->bindAt(context, address, type, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL, true);
1243				break;
1244			case BIND_OPCODE_SET_ADDEND_SLEB:
1245			case BIND_OPCODE_ADD_ADDR_ULEB:
1246			case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
1247			case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
1248			case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
1249			default:
1250				dyld::throwf("bad lazy bind opcode %d", *p);
1251		}
1252	}
1253
1254	if ( !this->usesTwoLevelNameSpace() ) {
1255		// release dyld global lock
1256		if ( unlock != NULL )
1257			unlock();
1258	}
1259	return result;
1260}
1261
1262void ImageLoaderMachOCompressed::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder)
1263{
1264	it.image = this;
1265	it.symbolName = " ";
1266	it.loadOrder = loadOrder;
1267	it.weakSymbol = false;
1268	it.symbolMatches = false;
1269	it.done = false;
1270	it.curIndex = 0;
1271	it.endIndex = this->fDyldInfo->weak_bind_size;
1272	it.address = 0;
1273	it.type = 0;
1274	it.addend = 0;
1275}
1276
1277
1278bool ImageLoaderMachOCompressed::incrementCoalIterator(CoalIterator& it)
1279{
1280	if ( it.done )
1281		return false;
1282
1283	if ( this->fDyldInfo->weak_bind_size == 0 ) {
1284		/// hmmm, ld set MH_WEAK_DEFINES or MH_BINDS_TO_WEAK, but there is no weak binding info
1285		it.done = true;
1286		it.symbolName = "~~~";
1287		return true;
1288	}
1289	const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off;
1290	const uint8_t* p = start + it.curIndex;
1291	const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size;
1292	uint32_t count;
1293	uint32_t skip;
1294	while ( p < end ) {
1295		uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
1296		uint8_t opcode = *p & BIND_OPCODE_MASK;
1297		++p;
1298		switch (opcode) {
1299			case BIND_OPCODE_DONE:
1300				it.done = true;
1301				it.curIndex = p - start;
1302				it.symbolName = "~~~"; // sorts to end
1303				return true;
1304			case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
1305				it.symbolName = (char*)p;
1306				it.weakSymbol = ((immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) == 0);
1307				it.symbolMatches = false;
1308				while (*p != '\0')
1309					++p;
1310				++p;
1311				it.curIndex = p - start;
1312				return false;
1313			case BIND_OPCODE_SET_TYPE_IMM:
1314				it.type = immediate;
1315				break;
1316			case BIND_OPCODE_SET_ADDEND_SLEB:
1317				it.addend = read_sleb128(p, end);
1318				break;
1319			case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
1320				if ( immediate > fSegmentsCount )
1321					dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n",
1322							immediate, fSegmentsCount);
1323				it.address = segActualLoadAddress(immediate) + read_uleb128(p, end);
1324				break;
1325			case BIND_OPCODE_ADD_ADDR_ULEB:
1326				it.address += read_uleb128(p, end);
1327				break;
1328			case BIND_OPCODE_DO_BIND:
1329				it.address += sizeof(intptr_t);
1330				break;
1331			case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
1332				it.address += read_uleb128(p, end) + sizeof(intptr_t);
1333				break;
1334			case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
1335				it.address += immediate*sizeof(intptr_t) + sizeof(intptr_t);
1336				break;
1337			case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
1338				count = read_uleb128(p, end);
1339				skip = read_uleb128(p, end);
1340				for (uint32_t i=0; i < count; ++i) {
1341					it.address += skip + sizeof(intptr_t);
1342				}
1343				break;
1344			default:
1345				dyld::throwf("bad weak bind opcode '%d' found after processing %d bytes in '%s'", *p, (int)(p-start), this->getPath());
1346		}
1347	}
1348	/// hmmm, BIND_OPCODE_DONE is missing...
1349	it.done = true;
1350	it.symbolName = "~~~";
1351	//dyld::log("missing BIND_OPCODE_DONE for image %s\n", this->getPath());
1352	return true;
1353}
1354
1355uintptr_t ImageLoaderMachOCompressed::getAddressCoalIterator(CoalIterator& it, const LinkContext& context)
1356{
1357	//dyld::log("looking for %s in %s\n", it.symbolName, this->getPath());
1358	const ImageLoader* foundIn = NULL;
1359	const ImageLoader::Symbol* sym = this->findExportedSymbol(it.symbolName, &foundIn);
1360	if ( sym != NULL ) {
1361		//dyld::log("sym=%p, foundIn=%p\n", sym, foundIn);
1362		return foundIn->getExportedSymbolAddress(sym, context, this);
1363	}
1364	return 0;
1365}
1366
1367
1368void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, const LinkContext& context)
1369{
1370	// <rdar://problem/6570879> weak binding done too early with inserted libraries
1371	if ( this->getState() < dyld_image_state_bound  )
1372		return;
1373
1374	const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off;
1375	const uint8_t* p = start + it.curIndex;
1376	const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size;
1377
1378	uint8_t type = it.type;
1379	uintptr_t address = it.address;
1380	const char* symbolName = it.symbolName;
1381	intptr_t addend = it.addend;
1382	uint32_t count;
1383	uint32_t skip;
1384	bool done = false;
1385	bool boundSomething = false;
1386	while ( !done && (p < end) ) {
1387		uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
1388		uint8_t opcode = *p & BIND_OPCODE_MASK;
1389		++p;
1390		switch (opcode) {
1391			case BIND_OPCODE_DONE:
1392				done = true;
1393				break;
1394			case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
1395				done = true;
1396				break;
1397			case BIND_OPCODE_SET_TYPE_IMM:
1398				type = immediate;
1399				break;
1400			case BIND_OPCODE_SET_ADDEND_SLEB:
1401				addend = read_sleb128(p, end);
1402				break;
1403			case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
1404				if ( immediate > fSegmentsCount )
1405					dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n",
1406							immediate, fSegmentsCount);
1407				address = segActualLoadAddress(immediate) + read_uleb128(p, end);
1408				break;
1409			case BIND_OPCODE_ADD_ADDR_ULEB:
1410				address += read_uleb128(p, end);
1411				break;
1412			case BIND_OPCODE_DO_BIND:
1413				bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak ");
1414				boundSomething = true;
1415				address += sizeof(intptr_t);
1416				break;
1417			case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
1418				bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak ");
1419				boundSomething = true;
1420				address += read_uleb128(p, end) + sizeof(intptr_t);
1421				break;
1422			case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
1423				bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak ");
1424				boundSomething = true;
1425				address += immediate*sizeof(intptr_t) + sizeof(intptr_t);
1426				break;
1427			case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
1428				count = read_uleb128(p, end);
1429				skip = read_uleb128(p, end);
1430				for (uint32_t i=0; i < count; ++i) {
1431					bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak ");
1432					boundSomething = true;
1433					address += skip + sizeof(intptr_t);
1434				}
1435				break;
1436			default:
1437				dyld::throwf("bad bind opcode %d in weak binding info", *p);
1438		}
1439	}
1440	// C++ weak coalescing cannot be tracked by reference counting.  Error on side of never unloading.
1441	if ( boundSomething && (targetImage != this) )
1442		context.addDynamicReference(this, targetImage);
1443}
1444
1445uintptr_t ImageLoaderMachOCompressed::interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*,
1446												uint8_t, intptr_t, int, const char*, LastLookup*, bool runResolver)
1447{
1448	if ( type == BIND_TYPE_POINTER ) {
1449		uintptr_t* fixupLocation = (uintptr_t*)addr;
1450		uintptr_t value = *fixupLocation;
1451		for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) {
1452			// replace all references to 'replacee' with 'replacement'
1453			if ( (value == it->replacee) && (this != it->replacementImage) ) {
1454				if ( context.verboseInterposing ) {
1455					dyld::log("dyld: interposing: at %p replace 0x%lX with 0x%lX in %s\n",
1456						fixupLocation, it->replacee, it->replacement, this->getPath());
1457				}
1458				*fixupLocation = it->replacement;
1459			}
1460		}
1461	}
1462	return 0;
1463}
1464
1465void ImageLoaderMachOCompressed::doInterpose(const LinkContext& context)
1466{
1467	if ( context.verboseInterposing )
1468		dyld::log("dyld: interposing %lu tuples onto image: %s\n", fgInterposingTuples.size(), this->getPath());
1469
1470	// update prebound symbols
1471	eachBind(context, &ImageLoaderMachOCompressed::interposeAt);
1472	eachLazyBind(context, &ImageLoaderMachOCompressed::interposeAt);
1473}
1474
1475
1476
1477
1478const char* ImageLoaderMachOCompressed::findClosestSymbol(const void* addr, const void** closestAddr) const
1479{
1480	// called by dladdr()
1481	// only works with compressed LINKEDIT if classic symbol table is also present
1482	const macho_nlist* symbolTable = NULL;
1483	const char* symbolTableStrings = NULL;
1484	const dysymtab_command* dynSymbolTable = NULL;
1485	const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
1486	const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
1487	const struct load_command* cmd = cmds;
1488	for (uint32_t i = 0; i < cmd_count; ++i) {
1489		switch (cmd->cmd) {
1490			case LC_SYMTAB:
1491				{
1492					const struct symtab_command* symtab = (struct symtab_command*)cmd;
1493					symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff];
1494					symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]);
1495				}
1496				break;
1497			case LC_DYSYMTAB:
1498				dynSymbolTable = (struct dysymtab_command*)cmd;
1499				break;
1500		}
1501		cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
1502	}
1503	// no symbol table => no lookup by address
1504	if ( (symbolTable == NULL) || (dynSymbolTable == NULL) )
1505		return NULL;
1506
1507	uintptr_t targetAddress = (uintptr_t)addr - fSlide;
1508	const struct macho_nlist* bestSymbol = NULL;
1509	// first walk all global symbols
1510	const struct macho_nlist* const globalsStart = &symbolTable[dynSymbolTable->iextdefsym];
1511	const struct macho_nlist* const globalsEnd= &globalsStart[dynSymbolTable->nextdefsym];
1512	for (const struct macho_nlist* s = globalsStart; s < globalsEnd; ++s) {
1513 		if ( (s->n_type & N_TYPE) == N_SECT ) {
1514			if ( bestSymbol == NULL ) {
1515				if ( s->n_value <= targetAddress )
1516					bestSymbol = s;
1517			}
1518			else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) {
1519				bestSymbol = s;
1520			}
1521		}
1522	}
1523	// next walk all local symbols
1524	const struct macho_nlist* const localsStart = &symbolTable[dynSymbolTable->ilocalsym];
1525	const struct macho_nlist* const localsEnd= &localsStart[dynSymbolTable->nlocalsym];
1526	for (const struct macho_nlist* s = localsStart; s < localsEnd; ++s) {
1527 		if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) {
1528			if ( bestSymbol == NULL ) {
1529				if ( s->n_value <= targetAddress )
1530					bestSymbol = s;
1531			}
1532			else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) {
1533				bestSymbol = s;
1534			}
1535		}
1536	}
1537	if ( bestSymbol != NULL ) {
1538#if __arm__
1539		if (bestSymbol->n_desc & N_ARM_THUMB_DEF)
1540			*closestAddr = (void*)((bestSymbol->n_value | 1) + fSlide);
1541		else
1542			*closestAddr = (void*)(bestSymbol->n_value + fSlide);
1543#else
1544		*closestAddr = (void*)(bestSymbol->n_value + fSlide);
1545#endif
1546		return &symbolTableStrings[bestSymbol->n_un.n_strx];
1547	}
1548	return NULL;
1549}
1550
1551
1552#if PREBOUND_IMAGE_SUPPORT
1553void ImageLoaderMachOCompressed::resetPreboundLazyPointers(const LinkContext& context)
1554{
1555	// no way to back off a prebound compress image
1556}
1557#endif
1558
1559
1560#if __arm__ || __x86_64__
1561void ImageLoaderMachOCompressed::updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr, const LinkContext& context)
1562{
1563#if __arm__
1564	uint32_t* instructions = (uint32_t*)stub;
1565    // sanity check this is a stub we understand
1566	if ( (instructions[0] != 0xe59fc004) || (instructions[1] != 0xe08fc00c) || (instructions[2] != 0xe59cf000) )
1567		return;
1568
1569	void** lazyPointerAddr = (void**)(instructions[3] + (stub + 12));
1570#endif
1571#if __x86_64__
1572    // sanity check this is a stub we understand
1573	if ( (stub[0] != 0xFF) || (stub[1] != 0x25) )
1574		return;
1575    int32_t ripOffset = *((int32_t*)(&stub[2]));
1576	void** lazyPointerAddr = (void**)(ripOffset + stub + 6);
1577#endif
1578
1579   // if stub does not use original lazy pointer (meaning it was optimized by update_dyld_shared_cache)
1580    if ( lazyPointerAddr != originalLazyPointerAddr ) {
1581		// <rdar://problem/12928448> only de-optimization lazy pointers if they are part of shared cache not loaded (because overridden)
1582		const ImageLoader* lazyPointerImage = context.findImageContainingAddress(lazyPointerAddr);
1583		if ( lazyPointerImage != NULL )
1584			return;
1585
1586        // copy newly re-bound lazy pointer value to shared lazy pointer
1587        *lazyPointerAddr = *originalLazyPointerAddr;
1588
1589		if ( context.verboseBind )
1590			dyld::log("dyld: alter bind: %s: *0x%08lX = 0x%08lX \n",
1591					  this->getShortName(), (long)lazyPointerAddr, (long)*originalLazyPointerAddr);
1592    }
1593}
1594#endif
1595
1596
1597// <rdar://problem/8890875> overriding shared cache dylibs with resolvers fails
1598void ImageLoaderMachOCompressed::updateOptimizedLazyPointers(const LinkContext& context)
1599{
1600#if __arm__ || __x86_64__
1601	// find stubs and lazy pointer sections
1602	const struct macho_section* stubsSection = NULL;
1603	const struct macho_section* lazyPointerSection = NULL;
1604	const dysymtab_command* dynSymbolTable = NULL;
1605	const macho_header* mh = (macho_header*)fMachOData;
1606	const uint32_t cmd_count = mh->ncmds;
1607	const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
1608	const struct load_command* cmd = cmds;
1609	for (uint32_t i = 0; i < cmd_count; ++i) {
1610		if (cmd->cmd == LC_SEGMENT_COMMAND) {
1611			const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
1612			const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
1613			const struct macho_section* const sectionsEnd = &sectionsStart[seg->nsects];
1614			for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
1615				const uint8_t type = sect->flags & SECTION_TYPE;
1616				if ( type == S_SYMBOL_STUBS )
1617					stubsSection = sect;
1618				else if ( type == S_LAZY_SYMBOL_POINTERS )
1619					lazyPointerSection = sect;
1620			}
1621		}
1622		else if ( cmd->cmd == LC_DYSYMTAB ) {
1623			dynSymbolTable = (struct dysymtab_command*)cmd;
1624		}
1625		cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
1626	}
1627
1628	// sanity check
1629	if ( dynSymbolTable == NULL )
1630		return;
1631	if ( (stubsSection == NULL) || (lazyPointerSection == NULL) )
1632		return;
1633	const uint32_t stubsCount = stubsSection->size / stubsSection->reserved2;
1634	const uint32_t lazyPointersCount = lazyPointerSection->size / sizeof(void*);
1635	if ( stubsCount != lazyPointersCount )
1636		return;
1637	const uint32_t stubsIndirectTableOffset = stubsSection->reserved1;
1638	const uint32_t lazyPointersIndirectTableOffset = lazyPointerSection->reserved1;
1639	if ( (stubsIndirectTableOffset+stubsCount) > dynSymbolTable->nindirectsyms )
1640		return;
1641	if ( (lazyPointersIndirectTableOffset+lazyPointersCount) > dynSymbolTable->nindirectsyms )
1642		return;
1643
1644	// walk stubs and lazy pointers
1645	const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff];
1646	void** const lazyPointersStartAddr = (void**)(lazyPointerSection->addr + this->fSlide);
1647	uint8_t* const stubsStartAddr = (uint8_t*)(stubsSection->addr + this->fSlide);
1648	uint8_t* stub = stubsStartAddr;
1649	void** lpa = lazyPointersStartAddr;
1650	for(uint32_t i=0; i < stubsCount; ++i, stub += stubsSection->reserved2, ++lpa) {
1651        // sanity check symbol index of stub and lazy pointer match
1652		if ( indirectTable[stubsIndirectTableOffset+i] != indirectTable[lazyPointersIndirectTableOffset+i] )
1653			continue;
1654		this->updateAlternateLazyPointer(stub, lpa, context);
1655	}
1656
1657#endif
1658}
1659
1660
1661