6
6
*
7
7
* ------------------------------------------------------
8
8
*
9
- * Copyright 2014 Kubo Takehiro <kubo@jiubao.org>
9
+ * Copyright 2014-2019 Kubo Takehiro <kubo@jiubao.org>
10
10
*
11
11
* Redistribution and use in source and binary forms, with or without modification, are
12
12
* permitted provided that the following conditions are met:
37
37
#include <stdarg.h>
38
38
#include <stdlib.h>
39
39
#include <string.h>
40
+ #include <inttypes.h>
40
41
#include <dlfcn.h>
41
42
#include <mach-o/dyld.h>
42
43
#include "plthook.h"
@@ -72,11 +73,21 @@ struct plthook {
72
73
bind_address_t entries [1 ];
73
74
};
74
75
75
- static int plthook_open_real (plthook_t * * plthook_out , const struct mach_header * mh );
76
- static unsigned int get_bind_addr (plthook_t * plthook , const uint8_t * base , uint32_t lazy_bind_off , uint32_t lazy_bind_size , struct segment_command_ * * segments , int addrdiff );
76
+ #define MAX_SEGMENTS 8
77
+
78
+ typedef struct {
79
+ plthook_t * plthook ;
80
+ intptr_t slide ;
81
+ int num_segments ;
82
+ int linkedit_segment_idx ;
83
+ struct segment_command_ * segments [MAX_SEGMENTS ];
84
+ } data_t ;
85
+
86
+ static int plthook_open_real (plthook_t * * plthook_out , uint32_t image_idx , const struct mach_header * mh , const char * image_name );
87
+ static unsigned int set_bind_addrs (data_t * d , uint32_t lazy_bind_off , uint32_t lazy_bind_size );
88
+ static void set_bind_addr (data_t * d , unsigned int * idx , const char * sym_name , int seg_index , int seg_offset );
77
89
78
90
static void set_errmsg (const char * fmt , ...) __attribute__((__format__ (__printf__ , 1 , 2 )));
79
- static void set_bind_addr (unsigned int * idx , plthook_t * plthook , const uint8_t * base , const char * sym_name , int seg_index , int seg_offset , struct segment_command_ * * segments );
80
91
81
92
static uint64_t uleb128 (const uint8_t * * p )
82
93
{
@@ -113,33 +124,40 @@ static char errmsg[512];
113
124
114
125
int plthook_open (plthook_t * * plthook_out , const char * filename )
115
126
{
116
- uint32_t idx = 0 ;
127
+ size_t namelen ;
128
+ uint32_t cnt ;
129
+ uint32_t idx ;
117
130
118
- if (filename != NULL ) {
119
- size_t namelen = strlen (filename );
120
-
121
- while (1 ) {
122
- const char * image_name = _dyld_get_image_name (idx );
123
- size_t offset = 0 ;
124
-
125
- if (image_name == NULL ) {
126
- * plthook_out = NULL ;
127
- set_errmsg ("Cannot find file: %s" , filename );
128
- return PLTHOOK_FILE_NOT_FOUND ;
129
- }
130
- if (* filename != '/' ) {
131
- size_t image_name_len = strlen (image_name );
132
- if (image_name_len > namelen ) {
133
- offset = image_name_len - namelen ;
134
- }
135
- }
136
- if (strcmp (image_name + offset , filename ) == 0 ) {
137
- break ;
131
+ if (filename == NULL ) {
132
+ return plthook_open_real (plthook_out , 0 , NULL , NULL );
133
+ }
134
+ cnt = _dyld_image_count ();
135
+ namelen = strlen (filename );
136
+ namelen = strlen (filename );
137
+ cnt = _dyld_image_count ();
138
+
139
+ for (idx = 0 ; idx < cnt ; idx ++ ) {
140
+ const char * image_name = _dyld_get_image_name (idx );
141
+ size_t offset = 0 ;
142
+
143
+ if (image_name == NULL ) {
144
+ * plthook_out = NULL ;
145
+ set_errmsg ("Cannot find file at image index %u" , idx );
146
+ return PLTHOOK_INTERNAL_ERROR ;
147
+ }
148
+ if (* filename != '/' ) {
149
+ size_t image_name_len = strlen (image_name );
150
+ if (image_name_len > namelen ) {
151
+ offset = image_name_len - namelen ;
138
152
}
139
- idx ++ ;
153
+ }
154
+ if (strcmp (image_name + offset , filename ) == 0 ) {
155
+ return plthook_open_real (plthook_out , idx , NULL , image_name );
140
156
}
141
157
}
142
- return plthook_open_real (plthook_out , _dyld_get_image_header (idx ));
158
+ * plthook_out = NULL ;
159
+ set_errmsg ("Cannot find file: %s" , filename );
160
+ return PLTHOOK_FILE_NOT_FOUND ;
143
161
}
144
162
145
163
int plthook_open_by_handle (plthook_t * * plthook_out , void * hndl )
@@ -149,25 +167,26 @@ int plthook_open_by_handle(plthook_t **plthook_out, void *hndl)
149
167
RTLD_LAZY | RTLD_NOLOAD | RTLD_FIRST ,
150
168
};
151
169
int flag_idx ;
170
+ uint32_t cnt = _dyld_image_count ();
152
171
#define NUM_FLAGS (sizeof(flags) / sizeof(flags[0]))
153
172
154
173
if (hndl == NULL ) {
155
174
set_errmsg ("NULL handle" );
156
175
return PLTHOOK_FILE_NOT_FOUND ;
157
176
}
158
177
for (flag_idx = 0 ; flag_idx < NUM_FLAGS ; flag_idx ++ ) {
159
- const char * image_name = NULL ;
160
- uint32_t idx = 0 ;
178
+ uint32_t idx ;
161
179
162
- do {
180
+ for (idx = 0 ; idx < cnt ; idx ++ ) {
181
+ const char * image_name = idx ? _dyld_get_image_name (idx ) : NULL ;
163
182
void * handle = dlopen (image_name , flags [flag_idx ]);
164
183
if (handle != NULL ) {
165
184
dlclose (handle );
166
185
if (handle == hndl ) {
167
- return plthook_open_real (plthook_out , _dyld_get_image_header ( idx ) );
186
+ return plthook_open_real (plthook_out , idx , NULL , image_name );
168
187
}
169
188
}
170
- } while (( image_name = _dyld_get_image_name ( ++ idx )) != NULL );
189
+ }
171
190
}
172
191
set_errmsg ("Cannot find the image correspond to handle %p" , hndl );
173
192
return PLTHOOK_FILE_NOT_FOUND ;
@@ -176,30 +195,43 @@ int plthook_open_by_handle(plthook_t **plthook_out, void *hndl)
176
195
int plthook_open_by_address (plthook_t * * plthook_out , void * address )
177
196
{
178
197
Dl_info dlinfo ;
198
+ uint32_t idx = 0 ;
199
+ uint32_t cnt = _dyld_image_count ();
179
200
180
201
if (!dladdr (address , & dlinfo )) {
181
202
* plthook_out = NULL ;
182
203
set_errmsg ("Cannot find address: %p" , address );
183
204
return PLTHOOK_FILE_NOT_FOUND ;
184
205
}
185
- return plthook_open_real (plthook_out , dlinfo .dli_fbase );
206
+ for (idx = 0 ; idx < cnt ; idx ++ ) {
207
+ if (dlinfo .dli_fbase == _dyld_get_image_header (idx )) {
208
+ return plthook_open_real (plthook_out , idx , dlinfo .dli_fbase , dlinfo .dli_fname );
209
+ }
210
+ }
211
+ set_errmsg ("Cannot find the image index for base address: %p" , dlinfo .dli_fbase );
212
+ return PLTHOOK_FILE_NOT_FOUND ;
186
213
}
187
214
188
- #define NUM_SEGMENTS 10
189
-
190
- static int plthook_open_real (plthook_t * * plthook_out , const struct mach_header * mh )
215
+ static int plthook_open_real (plthook_t * * plthook_out , uint32_t image_idx , const struct mach_header * mh , const char * image_name )
191
216
{
192
217
struct load_command * cmd ;
193
- const uint8_t * base = (const uint8_t * )mh ;
194
218
uint32_t lazy_bind_off = 0 ;
195
219
uint32_t lazy_bind_size = 0 ;
196
- struct segment_command_ * segments [NUM_SEGMENTS ];
197
- int segment_idx = 0 ;
198
220
unsigned int nbind ;
199
- int addrdiff = 0 ;
221
+ data_t data = {NULL ,};
222
+ size_t size ;
200
223
int i ;
201
224
202
- memset (segments , 0 , sizeof (segments ));
225
+ data .linkedit_segment_idx = -1 ;
226
+ data .slide = _dyld_get_image_vmaddr_slide (image_idx );
227
+ DEBUG_CMD ("slide=%" PRIxPTR "\n" , data .slide );
228
+ if (mh == NULL ) {
229
+ mh = _dyld_get_image_header (image_idx );
230
+ }
231
+ if (image_name == NULL ) {
232
+ image_name = _dyld_get_image_name (image_idx );
233
+ }
234
+
203
235
#ifdef __LP64__
204
236
cmd = (struct load_command * )((size_t )mh + sizeof (struct mach_header_64 ));
205
237
#else
@@ -224,11 +256,15 @@ static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *
224
256
segment -> fileoff , segment -> filesize ,
225
257
segment -> maxprot , segment -> initprot ,
226
258
segment -> nsects , segment -> flags );
259
+ #ifndef __LP64__
227
260
if (strcmp (segment -> segname , "__LINKEDIT" ) == 0 ) {
228
- addrdiff = segment -> vmaddr - segment -> fileoff ;
261
+ data . linkedit_segment_idx = data . num_segments ;
229
262
}
230
- #ifndef __LP64__
231
- segments [segment_idx ++ ] = segment ;
263
+ if (data .num_segments == MAX_SEGMENTS ) {
264
+ set_errmsg ("Too many segments: %s" , image_name );
265
+ return PLTHOOK_INTERNAL_ERROR ;
266
+ }
267
+ data .segments [data .num_segments ++ ] = segment ;
232
268
#endif
233
269
break ;
234
270
case LC_SEGMENT_64 : /* 0x19 */
@@ -244,11 +280,15 @@ static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *
244
280
segment64 -> fileoff , segment64 -> filesize ,
245
281
segment64 -> maxprot , segment64 -> initprot ,
246
282
segment64 -> nsects , segment64 -> flags );
283
+ #ifdef __LP64__
247
284
if (strcmp (segment64 -> segname , "__LINKEDIT" ) == 0 ) {
248
- addrdiff = segment64 -> vmaddr - segment64 -> fileoff ;
285
+ data . linkedit_segment_idx = data . num_segments ;
249
286
}
250
- #ifdef __LP64__
251
- segments [segment_idx ++ ] = segment64 ;
287
+ if (data .num_segments == MAX_SEGMENTS ) {
288
+ set_errmsg ("Too many segments: %s" , image_name );
289
+ return PLTHOOK_INTERNAL_ERROR ;
290
+ }
291
+ data .segments [data .num_segments ++ ] = segment64 ;
252
292
#endif
253
293
break ;
254
294
case LC_DYLD_INFO_ONLY : /* (0x22|LC_REQ_DYLD) */
@@ -312,30 +352,35 @@ static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *
312
352
}
313
353
cmd = (struct load_command * )((size_t )cmd + cmd -> cmdsize );
314
354
}
315
- nbind = get_bind_addr (NULL , base , lazy_bind_off , lazy_bind_size , segments , addrdiff );
316
- * plthook_out = (plthook_t * )malloc (offsetof(plthook_t , entries ) + sizeof (bind_address_t ) * nbind );
317
- (* plthook_out )-> num_entries = nbind ;
318
- get_bind_addr (* plthook_out , base , lazy_bind_off , lazy_bind_size , segments , addrdiff );
355
+ if (data .linkedit_segment_idx == -1 ) {
356
+ set_errmsg ("Cannot find the linkedit segment: %s" , image_name );
357
+ return PLTHOOK_INVALID_FILE_FORMAT ;
358
+ }
359
+ nbind = set_bind_addrs (& data , lazy_bind_off , lazy_bind_size );
360
+ size = offsetof(plthook_t , entries ) + sizeof (bind_address_t ) * nbind ;
361
+ data .plthook = (plthook_t * )malloc (size );
362
+ if (data .plthook == NULL ) {
363
+ set_errmsg ("failed to allocate memory: %" PRIuPTR " bytes" , size );
364
+ return PLTHOOK_OUT_OF_MEMORY ;
365
+ }
366
+ data .plthook -> num_entries = nbind ;
367
+ set_bind_addrs (& data , lazy_bind_off , lazy_bind_size );
319
368
369
+ * plthook_out = data .plthook ;
320
370
return 0 ;
321
371
}
322
372
323
- static unsigned int get_bind_addr ( plthook_t * plthook , const uint8_t * base , uint32_t lazy_bind_off , uint32_t lazy_bind_size , struct segment_command_ * * segments , int addrdiff )
373
+ static unsigned int set_bind_addrs ( data_t * data , uint32_t lazy_bind_off , uint32_t lazy_bind_size )
324
374
{
325
- const uint8_t * ptr = base + lazy_bind_off + addrdiff ;
375
+ struct segment_command_ * linkedit = data -> segments [data -> linkedit_segment_idx ];
376
+ const uint8_t * ptr = (uint8_t * )(linkedit -> vmaddr - linkedit -> fileoff + data -> slide + lazy_bind_off );
326
377
const uint8_t * end = ptr + lazy_bind_size ;
327
378
const char * sym_name ;
328
379
int seg_index = 0 ;
329
380
uint64_t seg_offset = 0 ;
330
381
int count , skip ;
331
- unsigned int idx ;
332
- DEBUG_BIND ("get_bind_addr(%p, 0x%x, 0x%x" , base , lazy_bind_off , lazy_bind_size );
333
- for (idx = 0 ; segments [idx ] != NULL ; idx ++ ) {
334
- DEBUG_BIND (", [%s]" , segments [idx ]-> segname );
335
- }
336
- DEBUG_BIND (")\n" );
382
+ unsigned int idx = 0 ;
337
383
338
- idx = 0 ;
339
384
while (ptr < end ) {
340
385
uint8_t op = * ptr & BIND_OPCODE_MASK ;
341
386
uint8_t imm = * ptr & BIND_IMMEDIATE_MASK ;
@@ -384,23 +429,23 @@ static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint3
384
429
DEBUG_BIND ("BIND_OPCODE_ADD_ADDR_ULEB: seg_offset = 0x%llx\n" , seg_offset );
385
430
break ;
386
431
case BIND_OPCODE_DO_BIND :
387
- set_bind_addr (& idx , plthook , base , sym_name , seg_index , seg_offset , segments );
432
+ set_bind_addr (data , & idx , sym_name , seg_index , seg_offset );
388
433
DEBUG_BIND ("BIND_OPCODE_DO_BIND\n" );
389
434
break ;
390
435
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB :
391
436
seg_offset += uleb128 (& ptr );
392
437
DEBUG_BIND ("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: seg_offset = 0x%llx\n" , seg_offset );
393
438
break ;
394
439
case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED :
395
- set_bind_addr (& idx , plthook , base , sym_name , seg_index , seg_offset , segments );
440
+ set_bind_addr (data , & idx , sym_name , seg_index , seg_offset );
396
441
seg_offset += imm * sizeof (void * );
397
442
DEBUG_BIND ("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED\n" );
398
443
break ;
399
444
case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB :
400
445
count = uleb128 (& ptr );
401
446
skip = uleb128 (& ptr );
402
447
for (i = 0 ; i < count ; i ++ ) {
403
- set_bind_addr (& idx , plthook , base , sym_name , seg_index , seg_offset , segments );
448
+ set_bind_addr (data , & idx , sym_name , seg_index , seg_offset );
404
449
seg_offset += skip ;
405
450
}
406
451
DEBUG_BIND ("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB\n" );
@@ -410,12 +455,12 @@ static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint3
410
455
return idx ;
411
456
}
412
457
413
- static void set_bind_addr (unsigned int * idx , plthook_t * plthook , const uint8_t * base , const char * sym_name , int seg_index , int seg_offset , struct segment_command_ * * segments )
458
+ static void set_bind_addr (data_t * data , unsigned int * idx , const char * sym_name , int seg_index , int seg_offset )
414
459
{
415
- if (plthook != NULL ) {
416
- uint32_t vmaddr = segments [seg_index ]-> vmaddr ;
417
- plthook -> entries [* idx ].name = sym_name ;
418
- plthook -> entries [* idx ].addr = (void * * )(base + vmaddr + seg_offset );
460
+ if (data -> plthook != NULL ) {
461
+ size_t vmaddr = data -> segments [seg_index ]-> vmaddr ;
462
+ data -> plthook -> entries [* idx ].name = sym_name ;
463
+ data -> plthook -> entries [* idx ].addr = (void * * )(vmaddr + data -> slide + seg_offset );
419
464
}
420
465
(* idx )++ ;
421
466
}
0 commit comments