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 = §ionsStart[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