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