3
3
#include <stdbool.h>
4
4
#include <dlfcn.h>
5
5
#include <pthread.h>
6
+ #include <sys/mman.h>
7
+ #include <limits.h>
8
+ #include <fcntl.h>
6
9
7
10
#include "substitute.h"
8
11
#include "substitute-internal.h"
12
+ #include "dyld_cache_format.h"
9
13
10
14
extern const struct dyld_all_image_infos * _dyld_get_all_image_infos ();
11
15
@@ -14,19 +18,190 @@ static pthread_once_t dyld_inspect_once = PTHREAD_ONCE_INIT;
14
18
static uintptr_t (* ImageLoaderMachO_getSlide )(void * );
15
19
static const struct mach_header * (* ImageLoaderMachO_machHeader )(void * );
16
20
17
- static void * sym_to_ptr (substitute_sym * sym , intptr_t slide ) {
21
+ static void * sym_to_ptr (const substitute_sym * sym , intptr_t slide ) {
18
22
uintptr_t addr = sym -> n_value ;
19
23
addr += slide ;
20
24
if (sym -> n_desc & N_ARM_THUMB_DEF )
21
25
addr |= 1 ;
22
26
return (void * ) addr ;
23
27
}
24
28
29
+ static const struct dyld_cache_header * _Atomic s_cur_shared_cache_hdr ;
30
+ static int s_cur_shared_cache_fd ;
31
+ static pthread_once_t s_open_cache_once = PTHREAD_ONCE_INIT ;
32
+ static struct dyld_cache_local_symbols_info s_cache_local_symbols_info ;
33
+ static struct dyld_cache_local_symbols_entry * s_cache_local_symbols_entries ;
34
+
35
+ static bool oscf_try_dir (const char * dir , const char * arch ,
36
+ const struct dyld_cache_header * dch ) {
37
+ char path [PATH_MAX ];
38
+ if (snprintf (path , sizeof (path ), "%s/%s%s" , dir ,
39
+ DYLD_SHARED_CACHE_BASE_NAME , arch ) >= sizeof (path ))
40
+ return false;
41
+ int fd = open (path , O_RDONLY );
42
+ if (fd < 0 )
43
+ return false;
44
+ struct dyld_cache_header this_dch ;
45
+ if (read (fd , & this_dch , sizeof (this_dch )) != sizeof (this_dch ))
46
+ goto fail ;
47
+ if (memcmp (this_dch .uuid , dch -> uuid , 16 ) ||
48
+ this_dch .localSymbolsSize != dch -> localSymbolsSize /* just in case */ )
49
+ goto fail ;
50
+ s_cur_shared_cache_fd = fd ;
51
+ struct dyld_cache_local_symbols_info * lsi = & s_cache_local_symbols_info ;
52
+ if (pread (fd , lsi , sizeof (* lsi ), dch -> localSymbolsOffset ) != sizeof (* lsi ))
53
+ goto fail ;
54
+ if (lsi -> nlistOffset > dch -> localSymbolsSize ||
55
+ lsi -> nlistCount > (dch -> localSymbolsSize - lsi -> nlistOffset )
56
+ / sizeof (substitute_sym ) ||
57
+ lsi -> stringsOffset > dch -> localSymbolsSize ||
58
+ lsi -> stringsSize > dch -> localSymbolsSize - lsi -> stringsOffset ) {
59
+ /* bad format */
60
+ goto fail ;
61
+ }
62
+ uint32_t count = lsi -> entriesCount ;
63
+ if (count > 1000000 )
64
+ goto fail ;
65
+ struct dyld_cache_local_symbols_entry * lses ;
66
+ size_t lses_size = count * sizeof (* lses );
67
+ if (!(lses = malloc (lses_size )))
68
+ goto fail ;
69
+ if (pread (fd , lses , lses_size , dch -> localSymbolsOffset + lsi -> entriesOffset )
70
+ != lses_size ) {
71
+ free (lses );
72
+ goto fail ;
73
+ }
74
+
75
+ s_cache_local_symbols_entries = lses ;
76
+ return true;
77
+
78
+ fail :
79
+ close (fd );
80
+ return false;
81
+ }
82
+
83
+ static void open_shared_cache_file_once () {
84
+ s_cur_shared_cache_fd = -1 ;
85
+ const struct dyld_cache_header * dch = s_cur_shared_cache_hdr ;
86
+ if (memcmp (dch -> magic , "dyld_v1 " , 8 ))
87
+ return ;
88
+ const char * archp = & dch -> magic [8 ];
89
+ while (* archp == ' ' )
90
+ archp ++ ;
91
+ static char filename [32 ];
92
+ const char * env_dir = getenv ("DYLD_SHARED_CACHE_DIR" );
93
+ if (env_dir ) {
94
+ if (oscf_try_dir (env_dir , archp , dch ))
95
+ return ;
96
+ }
97
+ #if __IPHONE_OS_VERSION_MIN_REQUIRED
98
+ oscf_try_dir (IPHONE_DYLD_SHARED_CACHE_DIR , archp , dch );
99
+ #else
100
+ oscf_try_dir (MACOSX_DYLD_SHARED_CACHE_DIR , archp , dch );
101
+ #endif
102
+ }
103
+
104
+ static bool ul_mmap (int fd , off_t offset , size_t size ,
105
+ void * data_p , void * * mapping_p , size_t * mapping_size_p ) {
106
+ int pmask = getpagesize () - 1 ;
107
+ int page_off = offset & pmask ;
108
+ off_t map_offset = offset & ~pmask ;
109
+ size_t map_size = ((offset + size + pmask ) & ~pmask ) - map_offset ;
110
+ void * mapping = mmap (NULL , map_size , PROT_READ , MAP_SHARED , fd , map_offset );
111
+ if (mapping == MAP_FAILED )
112
+ return false;
113
+ * (void * * ) data_p = (char * ) mapping + page_off ;
114
+ * mapping_p = mapping ;
115
+ * mapping_size_p = map_size ;
116
+ return true;
117
+ }
118
+
119
+ static bool get_shared_cache_syms (const void * hdr ,
120
+ const substitute_sym * * syms_p ,
121
+ const char * * strs_p ,
122
+ size_t * nsyms_p ,
123
+ void * * mapping_p ,
124
+ size_t * mapping_size_p ) {
125
+ pthread_once (& s_open_cache_once , open_shared_cache_file_once );
126
+ int fd = s_cur_shared_cache_fd ;
127
+ if (fd == -1 )
128
+ return false;
129
+ const struct dyld_cache_header * dch = s_cur_shared_cache_hdr ;
130
+ const struct dyld_cache_local_symbols_info * lsi = & s_cache_local_symbols_info ;
131
+ struct dyld_cache_local_symbols_entry lse ;
132
+ for (uint32_t i = 0 ; i < lsi -> entriesCount ; i ++ ) {
133
+ lse = s_cache_local_symbols_entries [i ];
134
+ if (lse .dylibOffset == (uintptr_t ) hdr - (uintptr_t ) dch )
135
+ goto got_lse ;
136
+ }
137
+ return false;
138
+ got_lse :
139
+ /* map - we don't do this persistently to avoid wasting address space on
140
+ * iOS (my random OS X 10.10 blob pushes 55MB) */
141
+ if (lse .nlistStartIndex > lsi -> nlistCount ||
142
+ lsi -> nlistCount - lse .nlistStartIndex < lse .nlistCount )
143
+ return false;
144
+
145
+ char * ls_data ;
146
+ if (!ul_mmap (fd , dch -> localSymbolsOffset , dch -> localSymbolsSize ,
147
+ & ls_data , mapping_p , mapping_size_p ))
148
+ return false;
149
+ const substitute_sym * syms = (void * ) (ls_data + lsi -> nlistOffset );
150
+ * syms_p = syms + lse .nlistStartIndex ;
151
+ * strs_p = ls_data + lsi -> stringsOffset ;
152
+ * nsyms_p = lse .nlistCount ;
153
+ return true;
154
+ }
155
+
156
+
157
+ static const struct dyld_cache_header * get_cur_shared_cache_hdr () {
158
+ const struct dyld_cache_header * dch = s_cur_shared_cache_hdr ;
159
+ if (!dch ) {
160
+ /* race is OK */
161
+ uint64_t start_address = 0 ;
162
+ if (syscall (294 , & start_address )) /* shared_region_check_np */
163
+ dch = (void * ) 1 ;
164
+ else
165
+ dch = (void * ) (uintptr_t ) start_address ;
166
+ s_cur_shared_cache_hdr = dch ;
167
+ }
168
+ return dch == (void * ) 1 ? NULL : dch ;
169
+ }
170
+
171
+ static bool addr_in_shared_cache (const void * addr ) {
172
+ const struct dyld_cache_header * dch = get_cur_shared_cache_hdr ();
173
+ if (!dch )
174
+ return false;
175
+
176
+ uint32_t mapping_count = dch -> mappingCount ;
177
+ const struct dyld_cache_mapping_info * mappings =
178
+ (void * ) ((char * ) dch + dch -> mappingOffset );
179
+ intptr_t slide = (uintptr_t ) dch - (uintptr_t ) mappings [0 ].address ;
180
+
181
+ for (uint32_t i = 0 ; i < mapping_count ; i ++ ) {
182
+ const struct dyld_cache_mapping_info * mapping = & mappings [i ];
183
+ uintptr_t diff = (uintptr_t ) addr -
184
+ ((uintptr_t ) mapping -> address + slide );
185
+ if (diff < mapping -> size )
186
+ return true;
187
+ }
188
+ return false;
189
+ }
190
+
25
191
static void find_syms_raw (const void * hdr , intptr_t * restrict slide ,
26
192
const char * * restrict names , void * * restrict syms ,
27
193
size_t nsyms ) {
28
194
memset (syms , 0 , sizeof (* syms ) * nsyms );
29
195
196
+ void * mapping = NULL ;
197
+ size_t mapping_size = 0 ;
198
+ const substitute_sym * cache_syms = NULL ;
199
+ const char * cache_strs = NULL ;
200
+ size_t ncache_syms = 0 ;
201
+ if (addr_in_shared_cache (hdr ))
202
+ get_shared_cache_syms (hdr , & cache_syms , & cache_strs , & ncache_syms ,
203
+ & mapping , & mapping_size );
204
+
30
205
/* note: no verification at all */
31
206
const mach_header_x * mh = hdr ;
32
207
uint32_t ncmds = mh -> ncmds ;
@@ -64,18 +239,28 @@ ok: ;
64
239
ok2 : ;
65
240
symtab = (void * ) symtab + * slide ;
66
241
strtab = (void * ) strtab + * slide ;
67
- /* This could be optimized for efficiency with a large number of names... */
68
- for (uint32_t i = 0 ; i < syc .nsyms ; i ++ ) {
69
- substitute_sym * sym = & symtab [i ];
70
- uint32_t strx = sym -> n_un .n_strx ;
71
- const char * name = strx == 0 ? "" : strtab + strx ;
72
- for (size_t j = 0 ; j < nsyms ; j ++ ) {
73
- if (!strcmp (name , names [j ])) {
74
- syms [j ] = sym_to_ptr (sym , * slide );
75
- break ;
242
+
243
+ for (int type = 0 ; type <= 1 ; type ++ ) {
244
+ const substitute_sym * this_symtab = type ? cache_syms : symtab ;
245
+ const char * this_strtab = type ? cache_strs : strtab ;
246
+ size_t this_nsyms = type ? ncache_syms : syc .nsyms ;
247
+ /* This could be optimized for efficiency with a large number of
248
+ * names... */
249
+ for (uint32_t i = 0 ; i < syc .nsyms ; i ++ ) {
250
+ const substitute_sym * sym = & this_symtab [i ];
251
+ uint32_t strx = sym -> n_un .n_strx ;
252
+ const char * name = strx == 0 ? "" : this_strtab + strx ;
253
+ for (size_t j = 0 ; j < nsyms ; j ++ ) {
254
+ if (!strcmp (name , names [j ])) {
255
+ syms [j ] = sym_to_ptr (sym , * slide );
256
+ break ;
257
+ }
76
258
}
77
259
}
78
260
}
261
+
262
+ if (mapping_size )
263
+ munmap (mapping , mapping_size );
79
264
}
80
265
81
266
/* This is a mess because the usual _dyld_image_count loop is not thread safe.
@@ -92,7 +277,8 @@ static void inspect_dyld() {
92
277
const struct dyld_all_image_infos * aii = _dyld_get_all_image_infos ();
93
278
const void * dyld_hdr = aii -> dyldImageLoadAddress ;
94
279
95
- const char * names [2 ] = { "__ZNK16ImageLoaderMachO8getSlideEv" , "__ZNK16ImageLoaderMachO10machHeaderEv" };
280
+ const char * names [2 ] = { "__ZNK16ImageLoaderMachO8getSlideEv" ,
281
+ "__ZNK16ImageLoaderMachO10machHeaderEv" };
96
282
void * syms [2 ];
97
283
intptr_t dyld_slide = -1 ;
98
284
find_syms_raw (dyld_hdr , & dyld_slide , names , syms , 2 );
0 commit comments