1/*
2 * Copyright (c) 2008-2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25#include <stddef.h>
26#include <string.h>
27#include <mach-o/loader.h>
28#include <mach-o/nlist.h>
29#include <stdlib.h>
30#include <dlfcn.h>
31#include <stdio.h>
32
33
34#ifndef LC_LAZY_LOAD_DYLIB
35	#define LC_LAZY_LOAD_DYLIB  0x20
36#endif
37#ifndef S_LAZY_DYLIB_SYMBOL_POINTERS
38	#define S_LAZY_DYLIB_SYMBOL_POINTERS  0x10
39#endif
40#ifndef LC_LOAD_UPWARD_DYLIB
41    #define	LC_LOAD_UPWARD_DYLIB (0x23 | LC_REQ_DYLD) /* load upward dylib */
42#endif
43
44#if __LP64__
45	#define LC_SEGMENT_COMMAND			LC_SEGMENT_64
46	#define LC_ROUTINES_COMMAND			LC_ROUTINES_64
47	typedef struct mach_header_64		macho_header;
48	typedef struct section_64			macho_section;
49	typedef struct nlist_64				macho_nlist;
50	typedef struct segment_command_64	macho_segment_command;
51#else
52	#define LC_SEGMENT_COMMAND		LC_SEGMENT
53	#define LC_ROUTINES_COMMAND		LC_ROUTINES
54	typedef struct mach_header		macho_header;
55	typedef struct section			macho_section;
56	typedef struct nlist			macho_nlist;
57	typedef struct segment_command	macho_segment_command;
58#endif
59
60extern const macho_header __dso_handle;
61
62
63// This function may be overriden by application code
64// to do custom error handling when a lazy symbol cannot be
65// resolved.
66int dyld_lazy_dylib_proxy() __attribute__((weak,visibility("hidden")));
67int dyld_lazy_dylib_proxy()
68{
69	return 0;
70}
71
72
73// This function may be overriden by application code
74// to dynamically change the path to a loaded lazy dylib.
75const char* dyld_lazy_dylib_path_fix(const char*) __attribute__((weak,visibility("hidden")));
76const char* dyld_lazy_dylib_path_fix(const char* path)
77{
78	return path;
79}
80
81
82static void* getHandleForLazyOrdinal(const macho_header* mh, void* handles[], uint8_t ordinal)
83{
84	const uint32_t cmd_count = mh->ncmds;
85	const struct load_command* const cmds = (struct load_command*)((char*)mh + sizeof(macho_header));
86	const struct load_command* cmd = cmds;
87	uint8_t loadDylibCount = 0;
88	uint8_t loadLazyDylibCount = 0;
89	uint32_t i;
90	// walk load commands to find LC_LAZY_LOAD_DYLIB that matches ordinal
91	for (i = 0; i < cmd_count; ++i) {
92		switch ( cmd->cmd ) {
93			case LC_LOAD_DYLIB:
94			case LC_LOAD_WEAK_DYLIB:
95			case LC_LOAD_UPWARD_DYLIB:
96				++loadDylibCount;
97				break;
98			case LC_LAZY_LOAD_DYLIB:
99				++loadDylibCount;
100				if ( loadDylibCount == ordinal ) {
101					if ( handles[loadLazyDylibCount] == NULL ) {
102						const struct dylib_command* dylib = (struct dylib_command*)cmd;
103						const char* path = (char*)cmd + dylib->dylib.name.offset;
104						const char* fixedPath = dyld_lazy_dylib_path_fix(path);
105						handles[loadLazyDylibCount] = dlopen(fixedPath, RTLD_LAZY);
106					}
107					return handles[loadLazyDylibCount];
108				}
109				++loadLazyDylibCount;
110				break;
111		}
112		cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
113	}
114	return NULL;
115}
116
117
118
119// called by dyld_lazy_dylib_stub_binding_helper
120// this function must figure out which function
121// lazyPointer is supposed to point to
122// and update it it.
123void* lazy_load_dylib(uintptr_t* lazyPointer) __attribute__((visibility("hidden")));
124void* lazy_load_dylib(uintptr_t* lazyPointer)
125{
126	static const macho_header*	mh = NULL;
127	static const macho_nlist*	symbolTable = NULL;
128	static const char*			stringTable = NULL;
129	static const uint8_t*		linkEditBase = NULL;
130	static const uint32_t*		indirectSymbolTable = NULL;
131	static intptr_t				slide = 0;
132	static void*				minHandles[8];
133	static void**				handles;
134
135	// do this work only on first call
136	uint32_t i;
137	if ( mh == NULL ) {
138		const macho_header* tmh = &__dso_handle;
139		// symbol table, indirect symbol table
140		const uint32_t cmd_count = tmh->ncmds;
141		const struct load_command* const cmds = (struct load_command*)((char*)tmh + sizeof(macho_header));
142		const struct load_command* cmd = cmds;
143		// first pass at load commands gets linkEditBase
144		for (i = 0; i < cmd_count; ++i) {
145			if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
146				const macho_segment_command* seg = (macho_segment_command*)cmd;
147				if ( strcmp(seg->segname,"__TEXT") == 0 )
148					slide = (uintptr_t)tmh - seg->vmaddr;
149				else if ( strcmp(seg->segname,"__LINKEDIT") == 0 )
150					linkEditBase = (uint8_t*)(seg->vmaddr + slide - seg->fileoff);
151			}
152			cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
153		}
154		// next pass at load commands gets symbolTable, stringTable
155		uint32_t lazyDylibCount = 0;
156		cmd = cmds;
157		for (i = 0; i < cmd_count; ++i) {
158			switch ( cmd->cmd ) {
159				case LC_SYMTAB:
160					{
161						const struct symtab_command* symtab = (struct symtab_command*)cmd;
162						stringTable = (const char*)&linkEditBase[symtab->stroff];
163						symbolTable = (macho_nlist*)(&linkEditBase[symtab->symoff]);
164					}
165					break;
166				case LC_DYSYMTAB:
167					{
168						const struct dysymtab_command* dsymtab = (struct dysymtab_command*)cmd;
169						indirectSymbolTable = (uint32_t*)(&linkEditBase[dsymtab->indirectsymoff]);
170					}
171					break;
172				case LC_LAZY_LOAD_DYLIB:
173					++lazyDylibCount;
174					break;
175			}
176			cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
177		}
178		// use static buffer when possible
179		if ( lazyDylibCount < 8 )
180			handles = minHandles;
181		else
182			handles = calloc(lazyDylibCount, sizeof(void*));
183
184		// save to static global to make this thread safe
185		mh = tmh;
186	}
187
188	// find lazy dylib pointer section
189	void* result = &dyld_lazy_dylib_proxy;
190	const uint32_t cmd_count = mh->ncmds;
191	const struct load_command* const cmds = (struct load_command*)((char*)mh + sizeof(macho_header));
192	const struct load_command* cmd = cmds;
193	// walk sections to find one with this lazy pointer
194	for (i = 0; i < cmd_count; ++i) {
195		if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
196			const macho_segment_command* seg = (macho_segment_command*)cmd;
197			const macho_section* const sectionsStart = (macho_section*)((char*)seg + sizeof(macho_segment_command));
198			const macho_section* const sectionsEnd = &sectionsStart[seg->nsects];
199			const macho_section* sect;
200			for (sect=sectionsStart; sect < sectionsEnd; ++sect) {
201				const uint8_t type = sect->flags & SECTION_TYPE;
202				if ( type == S_LAZY_DYLIB_SYMBOL_POINTERS ) { // S_LAZY_DYLIB_SYMBOL_POINTERS
203					const uint32_t pointerCount = sect->size / sizeof(uintptr_t);
204					uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + slide);
205					if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) {
206						const uint32_t indirectTableOffset = sect->reserved1;
207						const uint32_t lazyIndex = lazyPointer - symbolPointers;
208						uint32_t symbolIndex = indirectSymbolTable[indirectTableOffset + lazyIndex];
209						if ( symbolIndex != INDIRECT_SYMBOL_ABS && symbolIndex != INDIRECT_SYMBOL_LOCAL ) {
210							// found symbol for this lazy pointer, now lookup address
211							const char* symbolName = &stringTable[symbolTable[symbolIndex].n_un.n_strx];
212							uint8_t ordinal = GET_LIBRARY_ORDINAL(symbolTable[symbolIndex].n_desc);
213							void* handle = getHandleForLazyOrdinal(mh, handles, ordinal);
214							if ( handle != NULL ) {
215								void* addr = dlsym(handle, &symbolName[1]);
216								if ( addr != NULL )
217									result = addr;
218								*lazyPointer = (uintptr_t)result;
219								return result;
220							}
221						}
222					}
223				}
224			}
225		}
226		cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
227	}
228	*lazyPointer = (uintptr_t)result;
229	return result;
230}
231
232