ruby-oci8 2.2.12 → 2.2.13

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,12 +1,12 @@
1
1
  /* -*- indent-tabs-mode: nil -*-
2
2
  *
3
- * plthook_osx.c -- implemention of plthook for OS X
3
+ * plthook_osx.c -- implementation of plthook for OS X
4
4
  *
5
5
  * URL: https://github.com/kubo/plthook
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,12 +37,23 @@
37
37
  #include <stdarg.h>
38
38
  #include <stdlib.h>
39
39
  #include <string.h>
40
+ #include <unistd.h>
41
+ #include <inttypes.h>
40
42
  #include <dlfcn.h>
43
+ #include <errno.h>
41
44
  #include <mach-o/dyld.h>
45
+ #include <sys/mman.h>
46
+ #include <mach-o/fixup-chains.h>
42
47
  #include "plthook.h"
43
48
 
49
+ #if !defined(__x86_64__)
50
+ #error Not on macOS intel
51
+ #endif
52
+
44
53
  // #define PLTHOOK_DEBUG_CMD 1
45
54
  // #define PLTHOOK_DEBUG_BIND 1
55
+ // #define PLTHOOK_DEBUG_FIXUPS 1
56
+ // #define PLTHOOK_DEBUG_ADDR 1
46
57
 
47
58
  #ifdef PLTHOOK_DEBUG_CMD
48
59
  #define DEBUG_CMD(...) fprintf(stderr, __VA_ARGS__)
@@ -50,16 +61,90 @@
50
61
  #define DEBUG_CMD(...)
51
62
  #endif
52
63
 
64
+ #ifdef PLTHOOK_DEBUG_FIXUPS
65
+ #define DEBUG_FIXUPS(...) fprintf(stderr, __VA_ARGS__)
66
+ #else
67
+ #define DEBUG_FIXUPS(...)
68
+ #endif
69
+
53
70
  #ifdef PLTHOOK_DEBUG_BIND
54
71
  #define DEBUG_BIND(...) fprintf(stderr, __VA_ARGS__)
55
72
  #else
56
73
  #define DEBUG_BIND(...)
57
74
  #endif
58
75
 
59
- #ifdef __LP64__
60
- #define segment_command_ segment_command_64
61
- #else
62
- #define segment_command_ segment_command
76
+ #ifdef PLTHOOK_DEBUG_ADDR
77
+ #include <mach/mach.h>
78
+
79
+ #define INHERIT_MAX_SIZE 11
80
+ static char *inherit_to_str(vm_inherit_t inherit, char *buf)
81
+ {
82
+ switch (inherit) {
83
+ case VM_INHERIT_SHARE: return "share";
84
+ case VM_INHERIT_COPY: return "copy";
85
+ case VM_INHERIT_NONE: return "none";
86
+ case VM_INHERIT_DONATE_COPY: return "donate_copy";
87
+ default:
88
+ sprintf(buf, "%d", inherit);
89
+ return buf;
90
+ }
91
+ }
92
+
93
+ #define BEHAVIOR_MAX_SIZE 16
94
+ static char *behavior_to_str(vm_behavior_t behavior, char *buf)
95
+ {
96
+ switch (behavior) {
97
+ case VM_BEHAVIOR_DEFAULT: return "default";
98
+ case VM_BEHAVIOR_RANDOM: return "random";
99
+ case VM_BEHAVIOR_SEQUENTIAL: return "sequential";
100
+ case VM_BEHAVIOR_RSEQNTL: return "rseqntl";
101
+ case VM_BEHAVIOR_WILLNEED: return "willneed";
102
+ case VM_BEHAVIOR_DONTNEED: return "dontneed";
103
+ case VM_BEHAVIOR_FREE: return "free";
104
+ case VM_BEHAVIOR_ZERO_WIRED_PAGES: return "zero";
105
+ case VM_BEHAVIOR_REUSABLE: return "reusable";
106
+ case VM_BEHAVIOR_REUSE: return "reuse";
107
+ case VM_BEHAVIOR_CAN_REUSE: return "can";
108
+ case VM_BEHAVIOR_PAGEOUT: return "pageout";
109
+ default:
110
+ sprintf(buf, "%d", behavior);
111
+ return buf;
112
+ }
113
+ }
114
+
115
+ static void dump_maps(const char *image_name)
116
+ {
117
+ mach_port_t task = mach_task_self();
118
+ vm_region_basic_info_data_64_t info;
119
+ mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
120
+ memory_object_name_t object = 0;
121
+ vm_address_t addr = 0;
122
+ vm_size_t size;
123
+ char inherit_buf[INHERIT_MAX_SIZE + 1];
124
+ char behavior_buf[BEHAVIOR_MAX_SIZE + 1];
125
+
126
+ fprintf(stderr, "MEMORY MAP(%s)\n", image_name);
127
+ fprintf(stderr, " start address end address protection max_protection inherit shared reserved offset behavior user_wired_count\n");
128
+ while (vm_region_64(task, &addr, &size, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&info, &info_count, &object) == KERN_SUCCESS) {
129
+ fprintf(stderr, " %016lx-%016lx %c%c%c(%08x) %c%c%c(%08x) %-*s %c %c %08llx %-*s %u\n",
130
+ addr, addr + size,
131
+ (info.protection & VM_PROT_READ) ? 'r' : '-',
132
+ (info.protection & VM_PROT_WRITE) ? 'w' : '-',
133
+ (info.protection & VM_PROT_EXECUTE) ? 'x' : '-',
134
+ info.protection,
135
+ (info.max_protection & VM_PROT_READ) ? 'r' : '-',
136
+ (info.max_protection & VM_PROT_WRITE) ? 'w' : '-',
137
+ (info.max_protection & VM_PROT_EXECUTE) ? 'x' : '-',
138
+ info.max_protection,
139
+ INHERIT_MAX_SIZE, inherit_to_str(info.inheritance, inherit_buf),
140
+ info.shared ? 'Y' : 'N',
141
+ info.reserved ? 'Y' : 'N',
142
+ info.offset,
143
+ BEHAVIOR_MAX_SIZE, behavior_to_str(info.behavior, behavior_buf),
144
+ info.user_wired_count);
145
+ addr += size;
146
+ }
147
+ }
63
148
  #endif
64
149
 
65
150
  typedef struct {
@@ -69,14 +154,28 @@ typedef struct {
69
154
 
70
155
  struct plthook {
71
156
  unsigned int num_entries;
72
- bind_address_t entries[1];
157
+ int readonly_segment;
158
+ bind_address_t entries[1]; /* This must be the last. */
73
159
  };
74
160
 
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, ptrdiff_t addrdiff);
161
+ #define MAX_SEGMENTS 8
162
+
163
+ typedef struct {
164
+ plthook_t *plthook;
165
+ intptr_t slide;
166
+ int num_segments;
167
+ int linkedit_segment_idx;
168
+ struct segment_command_64 *segments[MAX_SEGMENTS];
169
+ struct linkedit_data_command *chained_fixups;
170
+ size_t got_addr;
171
+ } data_t;
172
+
173
+ static int plthook_open_real(plthook_t **plthook_out, uint32_t image_idx, const struct mach_header *mh, const char *image_name);
174
+ static unsigned int set_bind_addrs(data_t *d, uint32_t lazy_bind_off, uint32_t lazy_bind_size);
175
+ static void set_bind_addr(data_t *d, unsigned int *idx, const char *sym_name, int seg_index, int seg_offset);
176
+ static int read_chained_fixups(data_t *d, const struct mach_header *mh, const char *image_name);
77
177
 
78
178
  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, uint64_t seg_offset, struct segment_command_ **segments);
80
179
 
81
180
  static uint64_t uleb128(const uint8_t **p)
82
181
  {
@@ -113,33 +212,40 @@ static char errmsg[512];
113
212
 
114
213
  int plthook_open(plthook_t **plthook_out, const char *filename)
115
214
  {
116
- uint32_t idx = 0;
215
+ size_t namelen;
216
+ uint32_t cnt;
217
+ uint32_t idx;
117
218
 
118
- if (filename != NULL) {
119
- size_t namelen = strlen(filename);
219
+ if (filename == NULL) {
220
+ return plthook_open_real(plthook_out, 0, NULL, NULL);
221
+ }
222
+ cnt = _dyld_image_count();
223
+ namelen = strlen(filename);
224
+ namelen = strlen(filename);
225
+ cnt = _dyld_image_count();
120
226
 
121
- while (1) {
122
- const char *image_name = _dyld_get_image_name(idx);
123
- size_t offset = 0;
227
+ for (idx = 0; idx < cnt; idx++) {
228
+ const char *image_name = _dyld_get_image_name(idx);
229
+ size_t offset = 0;
124
230
 
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;
231
+ if (image_name == NULL) {
232
+ *plthook_out = NULL;
233
+ set_errmsg("Cannot find file at image index %u", idx);
234
+ return PLTHOOK_INTERNAL_ERROR;
235
+ }
236
+ if (*filename != '/') {
237
+ size_t image_name_len = strlen(image_name);
238
+ if (image_name_len > namelen) {
239
+ offset = image_name_len - namelen;
138
240
  }
139
- idx++;
241
+ }
242
+ if (strcmp(image_name + offset, filename) == 0) {
243
+ return plthook_open_real(plthook_out, idx, NULL, image_name);
140
244
  }
141
245
  }
142
- return plthook_open_real(plthook_out, _dyld_get_image_header(idx));
246
+ *plthook_out = NULL;
247
+ set_errmsg("Cannot find file: %s", filename);
248
+ return PLTHOOK_FILE_NOT_FOUND;
143
249
  }
144
250
 
145
251
  int plthook_open_by_handle(plthook_t **plthook_out, void *hndl)
@@ -148,7 +254,8 @@ int plthook_open_by_handle(plthook_t **plthook_out, void *hndl)
148
254
  RTLD_LAZY | RTLD_NOLOAD,
149
255
  RTLD_LAZY | RTLD_NOLOAD | RTLD_FIRST,
150
256
  };
151
- size_t flag_idx;
257
+ int flag_idx;
258
+ uint32_t cnt = _dyld_image_count();
152
259
  #define NUM_FLAGS (sizeof(flags) / sizeof(flags[0]))
153
260
 
154
261
  if (hndl == NULL) {
@@ -156,18 +263,18 @@ int plthook_open_by_handle(plthook_t **plthook_out, void *hndl)
156
263
  return PLTHOOK_FILE_NOT_FOUND;
157
264
  }
158
265
  for (flag_idx = 0; flag_idx < NUM_FLAGS; flag_idx++) {
159
- const char *image_name = NULL;
160
- uint32_t idx = 0;
266
+ uint32_t idx;
161
267
 
162
- do {
268
+ for (idx = 0; idx < cnt; idx++) {
269
+ const char *image_name = idx ? _dyld_get_image_name(idx) : NULL;
163
270
  void *handle = dlopen(image_name, flags[flag_idx]);
164
271
  if (handle != NULL) {
165
272
  dlclose(handle);
166
273
  if (handle == hndl) {
167
- return plthook_open_real(plthook_out, _dyld_get_image_header(idx));
274
+ return plthook_open_real(plthook_out, idx, NULL, image_name);
168
275
  }
169
276
  }
170
- } while ((image_name = _dyld_get_image_name(++idx)) != NULL);
277
+ }
171
278
  }
172
279
  set_errmsg("Cannot find the image correspond to handle %p", hndl);
173
280
  return PLTHOOK_FILE_NOT_FOUND;
@@ -176,43 +283,62 @@ int plthook_open_by_handle(plthook_t **plthook_out, void *hndl)
176
283
  int plthook_open_by_address(plthook_t **plthook_out, void *address)
177
284
  {
178
285
  Dl_info dlinfo;
286
+ uint32_t idx = 0;
287
+ uint32_t cnt = _dyld_image_count();
179
288
 
180
289
  if (!dladdr(address, &dlinfo)) {
181
290
  *plthook_out = NULL;
182
291
  set_errmsg("Cannot find address: %p", address);
183
292
  return PLTHOOK_FILE_NOT_FOUND;
184
293
  }
185
- return plthook_open_real(plthook_out, dlinfo.dli_fbase);
294
+ for (idx = 0; idx < cnt; idx++) {
295
+ if (dlinfo.dli_fbase == _dyld_get_image_header(idx)) {
296
+ return plthook_open_real(plthook_out, idx, dlinfo.dli_fbase, dlinfo.dli_fname);
297
+ }
298
+ }
299
+ set_errmsg("Cannot find the image index for base address: %p", dlinfo.dli_fbase);
300
+ return PLTHOOK_FILE_NOT_FOUND;
186
301
  }
187
302
 
188
- #define NUM_SEGMENTS 10
189
-
190
- static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *mh)
303
+ static int plthook_open_real(plthook_t **plthook_out, uint32_t image_idx, const struct mach_header *mh, const char *image_name)
191
304
  {
192
305
  struct load_command *cmd;
193
- const uint8_t *base = (const uint8_t *)mh;
194
306
  uint32_t lazy_bind_off = 0;
195
307
  uint32_t lazy_bind_size = 0;
196
- struct segment_command_ *segments[NUM_SEGMENTS];
197
- int segment_idx = 0;
198
308
  unsigned int nbind;
199
- ptrdiff_t addrdiff = 0;
200
- uint32_t i;
309
+ data_t data = {NULL,};
310
+ size_t size;
311
+ int i;
201
312
 
202
- memset(segments, 0, sizeof(segments));
203
- #ifdef __LP64__
204
- cmd = (struct load_command *)((size_t)mh + sizeof(struct mach_header_64));
205
- #else
206
- cmd = (struct load_command *)((size_t)mh + sizeof(struct mach_header));
313
+ data.linkedit_segment_idx = -1;
314
+ data.slide = _dyld_get_image_vmaddr_slide(image_idx);
315
+ if (mh == NULL) {
316
+ mh = _dyld_get_image_header(image_idx);
317
+ }
318
+ if (image_name == NULL) {
319
+ image_name = _dyld_get_image_name(image_idx);
320
+ }
321
+ #if defined(PLTHOOK_DEBUG_CMD) || defined(PLTHOOK_DEBUG_ADDR)
322
+ fprintf(stderr, "mh=%"PRIxPTR" slide=%"PRIxPTR"\n", (uintptr_t)mh, data.slide);
323
+ #endif
324
+ #ifdef PLTHOOK_DEBUG_ADDR
325
+ dump_maps(image_name);
207
326
  #endif
327
+
328
+ cmd = (struct load_command *)((size_t)mh + sizeof(struct mach_header_64));
329
+ DEBUG_CMD("CMD START\n");
208
330
  for (i = 0; i < mh->ncmds; i++) {
209
331
  struct dyld_info_command *dyld_info;
332
+ #ifdef PLTHOOK_DEBUG_CMD
210
333
  struct segment_command *segment;
334
+ #endif
211
335
  struct segment_command_64 *segment64;
212
336
 
213
337
  switch (cmd->cmd) {
214
338
  case LC_SEGMENT: /* 0x1 */
339
+ #ifdef PLTHOOK_DEBUG_CMD
215
340
  segment = (struct segment_command *)cmd;
341
+ #endif
216
342
  DEBUG_CMD("LC_SEGMENT\n"
217
343
  " segname %s\n"
218
344
  " vmaddr %8x vmsize %8x\n"
@@ -224,12 +350,6 @@ static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *
224
350
  segment->fileoff, segment->filesize,
225
351
  segment->maxprot, segment->initprot,
226
352
  segment->nsects, segment->flags);
227
- if (strcmp(segment->segname, "__LINKEDIT") == 0) {
228
- addrdiff = segment->vmaddr - segment->fileoff;
229
- }
230
- #ifndef __LP64__
231
- segments[segment_idx++] = segment;
232
- #endif
233
353
  break;
234
354
  case LC_SEGMENT_64: /* 0x19 */
235
355
  segment64 = (struct segment_command_64 *)cmd;
@@ -245,11 +365,49 @@ static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *
245
365
  segment64->maxprot, segment64->initprot,
246
366
  segment64->nsects, segment64->flags);
247
367
  if (strcmp(segment64->segname, "__LINKEDIT") == 0) {
248
- addrdiff = segment64->vmaddr - segment64->fileoff;
368
+ data.linkedit_segment_idx = data.num_segments;
249
369
  }
250
- #ifdef __LP64__
251
- segments[segment_idx++] = segment64;
252
- #endif
370
+ if (strcmp(segment64->segname, "__DATA_CONST") == 0) {
371
+ struct section_64 *sec = (struct section_64 *)(segment64 + 1);
372
+ uint32_t i;
373
+ for (i = 0; i < segment64->nsects; i++) {
374
+ DEBUG_CMD(" section_64 (%u)\n"
375
+ " sectname %s\n"
376
+ " segname %s\n"
377
+ " addr 0x%llx\n"
378
+ " size 0x%llx\n"
379
+ " offset 0x%x\n"
380
+ " align 0x%x\n"
381
+ " reloff 0x%x\n"
382
+ " nreloc %d\n"
383
+ " flags 0x%x\n"
384
+ " reserved1 %d\n"
385
+ " reserved2 %d\n"
386
+ " reserved3 %d\n",
387
+ i,
388
+ sec->sectname,
389
+ sec->segname,
390
+ sec->addr,
391
+ sec->size,
392
+ sec->offset,
393
+ sec->align,
394
+ sec->reloff,
395
+ sec->nreloc,
396
+ sec->flags,
397
+ sec->reserved1,
398
+ sec->reserved2,
399
+ sec->reserved3);
400
+ if (strcmp(sec->segname, "__DATA_CONST") == 0 && strcmp(sec->sectname, "__got") == 0) {
401
+ data.got_addr = sec->addr + data.slide;
402
+ }
403
+ sec++;
404
+ }
405
+ }
406
+ if (data.num_segments == MAX_SEGMENTS) {
407
+ set_errmsg("Too many segments: %s", image_name);
408
+ return PLTHOOK_INTERNAL_ERROR;
409
+ }
410
+ data.segments[data.num_segments++] = segment64;
253
411
  break;
254
412
  case LC_DYLD_INFO_ONLY: /* (0x22|LC_REQ_DYLD) */
255
413
  dyld_info= (struct dyld_info_command *)cmd;
@@ -289,6 +447,9 @@ static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *
289
447
  case LC_UUID: /* 0x1b */
290
448
  DEBUG_CMD("LC_UUID\n");
291
449
  break;
450
+ case LC_CODE_SIGNATURE: /* 0x1d */
451
+ DEBUG_CMD("LC_CODE_SIGNATURE\n");
452
+ break;
292
453
  case LC_VERSION_MIN_MACOSX: /* 0x24 */
293
454
  DEBUG_CMD("LC_VERSION_MIN_MACOSX\n");
294
455
  break;
@@ -307,41 +468,69 @@ static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *
307
468
  case LC_DYLIB_CODE_SIGN_DRS: /* 0x2B */
308
469
  DEBUG_CMD("LC_DYLIB_CODE_SIGN_DRS\n");
309
470
  break;
471
+ case LC_BUILD_VERSION: /* 0x32 */
472
+ DEBUG_CMD("LC_BUILD_VERSION\n");
473
+ break;
474
+ case LC_DYLD_EXPORTS_TRIE: /* (0x33|LC_REQ_DYLD) */
475
+ DEBUG_CMD("LC_DYLD_EXPORTS_TRIE\n");
476
+ break;
477
+ case LC_DYLD_CHAINED_FIXUPS: /* (0x34|LC_REQ_DYLD) */
478
+ data.chained_fixups = (struct linkedit_data_command *)cmd;
479
+ DEBUG_CMD("LC_DYLD_CHAINED_FIXUPS\n"
480
+ " cmdsize %u\n"
481
+ " dataoff %u (0x%x)\n"
482
+ " datasize %u\n",
483
+ data.chained_fixups->cmdsize,
484
+ data.chained_fixups->dataoff,
485
+ data.chained_fixups->dataoff,
486
+ data.chained_fixups->datasize);
487
+ break;
310
488
  default:
311
489
  DEBUG_CMD("LC_? (0x%x)\n", cmd->cmd);
312
490
  }
313
491
  cmd = (struct load_command *)((size_t)cmd + cmd->cmdsize);
314
492
  }
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);
493
+ DEBUG_CMD("CMD END\n");
494
+ if (data.linkedit_segment_idx == -1) {
495
+ set_errmsg("Cannot find the linkedit segment: %s", image_name);
496
+ return PLTHOOK_INVALID_FILE_FORMAT;
497
+ }
498
+ if (data.chained_fixups != NULL) {
499
+ int rv = read_chained_fixups(&data, mh, image_name);
500
+ if (rv != 0) {
501
+ return rv;
502
+ }
503
+ } else {
504
+ nbind = set_bind_addrs(&data, lazy_bind_off, lazy_bind_size);
505
+ size = offsetof(plthook_t, entries) + sizeof(bind_address_t) * nbind;
506
+ data.plthook = (plthook_t*)calloc(1, size);
507
+ if (data.plthook == NULL) {
508
+ set_errmsg("failed to allocate memory: %" PRIuPTR " bytes", size);
509
+ return PLTHOOK_OUT_OF_MEMORY;
510
+ }
511
+ data.plthook->num_entries = nbind;
512
+ set_bind_addrs(&data, lazy_bind_off, lazy_bind_size);
513
+ }
319
514
 
515
+ *plthook_out = data.plthook;
320
516
  return 0;
321
517
  }
322
518
 
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, ptrdiff_t addrdiff)
519
+ static unsigned int set_bind_addrs(data_t *data, uint32_t lazy_bind_off, uint32_t lazy_bind_size)
324
520
  {
325
- const uint8_t *ptr = base + lazy_bind_off + addrdiff;
521
+ struct segment_command_64 *linkedit = data->segments[data->linkedit_segment_idx];
522
+ const uint8_t *ptr = (uint8_t*)(linkedit->vmaddr - linkedit->fileoff + data->slide + lazy_bind_off);
326
523
  const uint8_t *end = ptr + lazy_bind_size;
327
524
  const char *sym_name;
328
525
  int seg_index = 0;
329
526
  uint64_t seg_offset = 0;
330
- uint64_t 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");
527
+ int count, skip;
528
+ unsigned int idx = 0;
337
529
 
338
- idx = 0;
339
530
  while (ptr < end) {
340
531
  uint8_t op = *ptr & BIND_OPCODE_MASK;
341
532
  uint8_t imm = *ptr & BIND_IMMEDIATE_MASK;
342
- uint64_t ulebval;
343
- int64_t slebval;
344
- uint64_t i;
533
+ int i;
345
534
 
346
535
  DEBUG_BIND("0x%02x: ", *ptr);
347
536
  ptr++;
@@ -353,8 +542,11 @@ static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint3
353
542
  DEBUG_BIND("BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: ordinal = %u\n", imm);
354
543
  break;
355
544
  case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
356
- ulebval = uleb128(&ptr);
357
- DEBUG_BIND("BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: ordinal = %llu\n", ulebval);
545
+ #ifdef PLTHOOK_DEBUG_BIND
546
+ DEBUG_BIND("BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: ordinal = %llu\n", uleb128(&ptr));
547
+ #else
548
+ uleb128(&ptr);
549
+ #endif
358
550
  break;
359
551
  case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
360
552
  if (imm == 0) {
@@ -371,8 +563,11 @@ static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint3
371
563
  DEBUG_BIND("BIND_OPCODE_SET_TYPE_IMM: type = %u\n", imm);
372
564
  break;
373
565
  case BIND_OPCODE_SET_ADDEND_SLEB:
374
- slebval = sleb128(&ptr);
375
- DEBUG_BIND("BIND_OPCODE_SET_ADDEND_SLEB: ordinal = %lld\n", slebval);
566
+ #ifdef PLTHOOK_DEBUG_BIND
567
+ DEBUG_BIND("BIND_OPCODE_SET_ADDEND_SLEB: ordinal = %lld\n", sleb128(&ptr));
568
+ #else
569
+ sleb128(&ptr);
570
+ #endif
376
571
  break;
377
572
  case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
378
573
  seg_index = imm;
@@ -384,7 +579,7 @@ static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint3
384
579
  DEBUG_BIND("BIND_OPCODE_ADD_ADDR_ULEB: seg_offset = 0x%llx\n", seg_offset);
385
580
  break;
386
581
  case BIND_OPCODE_DO_BIND:
387
- set_bind_addr(&idx, plthook, base, sym_name, seg_index, seg_offset, segments);
582
+ set_bind_addr(data, &idx, sym_name, seg_index, seg_offset);
388
583
  DEBUG_BIND("BIND_OPCODE_DO_BIND\n");
389
584
  break;
390
585
  case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
@@ -392,7 +587,7 @@ static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint3
392
587
  DEBUG_BIND("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: seg_offset = 0x%llx\n", seg_offset);
393
588
  break;
394
589
  case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
395
- set_bind_addr(&idx, plthook, base, sym_name, seg_index, seg_offset, segments);
590
+ set_bind_addr(data, &idx, sym_name, seg_index, seg_offset);
396
591
  seg_offset += imm * sizeof(void *);
397
592
  DEBUG_BIND("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED\n");
398
593
  break;
@@ -400,7 +595,7 @@ static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint3
400
595
  count = uleb128(&ptr);
401
596
  skip = uleb128(&ptr);
402
597
  for (i = 0; i < count; i++) {
403
- set_bind_addr(&idx, plthook, base, sym_name, seg_index, seg_offset, segments);
598
+ set_bind_addr(data, &idx, sym_name, seg_index, seg_offset);
404
599
  seg_offset += skip;
405
600
  }
406
601
  DEBUG_BIND("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB\n");
@@ -410,16 +605,368 @@ static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint3
410
605
  return idx;
411
606
  }
412
607
 
413
- static void set_bind_addr(unsigned int *idx, plthook_t *plthook, const uint8_t *base, const char *sym_name, int seg_index, uint64_t seg_offset, struct segment_command_ **segments)
608
+ static void set_bind_addr(data_t *data, unsigned int *idx, const char *sym_name, int seg_index, int seg_offset)
414
609
  {
415
- if (plthook != NULL) {
416
- uint64_t vmaddr = segments[seg_index]->vmaddr;
417
- plthook->entries[*idx].name = sym_name;
418
- plthook->entries[*idx].addr = (void**)(base + vmaddr + seg_offset);
610
+ if (data->plthook != NULL) {
611
+ size_t vmaddr = data->segments[seg_index]->vmaddr;
612
+ data->plthook->entries[*idx].name = sym_name;
613
+ data->plthook->entries[*idx].addr = (void**)(vmaddr + data->slide + seg_offset);
419
614
  }
420
615
  (*idx)++;
421
616
  }
422
617
 
618
+ static int read_chained_fixups(data_t *d, const struct mach_header *mh, const char *image_name)
619
+ {
620
+ const uint8_t *ptr = (const uint8_t *)mh + d->chained_fixups->dataoff;
621
+ const uint8_t *end = ptr + d->chained_fixups->datasize;
622
+ const struct dyld_chained_fixups_header *header = (const struct dyld_chained_fixups_header *)ptr;
623
+ const struct dyld_chained_import *import = (const struct dyld_chained_import *)(ptr + header->imports_offset);
624
+ const struct dyld_chained_import_addend *import_addend = (const struct dyld_chained_import_addend *)(ptr + header->imports_offset);
625
+ const struct dyld_chained_import_addend64 *import_addend64 = (const struct dyld_chained_import_addend64 *)(ptr + header->imports_offset);
626
+ const char *symbol_pool = (const char*)ptr + header->symbols_offset;
627
+ int rv = PLTHOOK_INTERNAL_ERROR;
628
+ size_t size;
629
+ uint32_t i;
630
+ #ifdef PLTHOOK_DEBUG_FIXUPS
631
+ const struct dyld_chained_starts_in_image *starts = (const struct dyld_chained_starts_in_image *)(ptr + header->starts_offset);
632
+ FILE *fp = NULL;
633
+ #endif
634
+ if (d->got_addr == 0) {
635
+ set_errmsg("__got section is not found in %s", image_name);
636
+ rv = PLTHOOK_INVALID_FILE_FORMAT;
637
+ goto cleanup;
638
+ }
639
+
640
+ DEBUG_FIXUPS("dyld_chained_fixups_header\n"
641
+ " fixups_version %u\n"
642
+ " starts_offset %u\n"
643
+ " imports_offset %u\n"
644
+ " symbols_offset %u\n"
645
+ " imports_count %u\n"
646
+ " imports_format %u\n"
647
+ " symbols_format %u\n",
648
+ header->fixups_version,
649
+ header->starts_offset,
650
+ header->imports_offset,
651
+ header->symbols_offset,
652
+ header->imports_count,
653
+ header->imports_format,
654
+ header->symbols_format);
655
+ if (header->fixups_version != 0) {
656
+ set_errmsg("unknown chained fixups version %u", header->fixups_version);
657
+ rv = PLTHOOK_INVALID_FILE_FORMAT;
658
+ goto cleanup;
659
+ }
660
+
661
+ size = offsetof(plthook_t, entries) + sizeof(bind_address_t) * header->imports_count;
662
+ d->plthook = (plthook_t*)calloc(1, size);
663
+ if (d->plthook == NULL) {
664
+ set_errmsg("failed to allocate memory: %" PRIuPTR " bytes", size);
665
+ rv = PLTHOOK_OUT_OF_MEMORY;
666
+ goto cleanup;
667
+ }
668
+ d->plthook->num_entries = header->imports_count;
669
+ d->plthook->readonly_segment = 1;
670
+
671
+ switch (header->imports_format) {
672
+ case DYLD_CHAINED_IMPORT:
673
+ DEBUG_FIXUPS("dyld_chained_import\n");
674
+ break;
675
+ case DYLD_CHAINED_IMPORT_ADDEND:
676
+ DEBUG_FIXUPS("dyld_chained_import_addend\n");
677
+ break;
678
+ case DYLD_CHAINED_IMPORT_ADDEND64:
679
+ DEBUG_FIXUPS("dyld_chained_import_addend64\n");
680
+ break;
681
+ default:
682
+ set_errmsg("unknown imports format %u", header->imports_format);
683
+ rv = PLTHOOK_INVALID_FILE_FORMAT;
684
+ goto cleanup;
685
+ }
686
+
687
+ for (i = 0; i < header->imports_count; i++) {
688
+ struct dyld_chained_import_addend64 imp;
689
+ switch (header->imports_format) {
690
+ case DYLD_CHAINED_IMPORT:
691
+ imp.lib_ordinal = import[i].lib_ordinal;
692
+ imp.weak_import = import[i].weak_import;
693
+ imp.name_offset = import[i].name_offset;
694
+ imp.addend = 0;
695
+ break;
696
+ case DYLD_CHAINED_IMPORT_ADDEND:
697
+ imp.lib_ordinal = import_addend[i].lib_ordinal;
698
+ imp.weak_import = import_addend[i].weak_import;
699
+ imp.name_offset = import_addend[i].name_offset;
700
+ imp.addend = import_addend[i].addend;
701
+ break;
702
+ case DYLD_CHAINED_IMPORT_ADDEND64:
703
+ imp = import_addend64[i];
704
+ break;
705
+ }
706
+ const char *name = symbol_pool + imp.name_offset;
707
+ if (name > (const char*)end) {
708
+ DEBUG_FIXUPS(" lib_ordinal %u, weak_import %u, name_offset %u, addend %llu\n",
709
+ imp.lib_ordinal, imp.weak_import, imp.name_offset, imp.addend);
710
+ set_errmsg("invalid symbol name address");
711
+ rv = PLTHOOK_INVALID_FILE_FORMAT;
712
+ goto cleanup;
713
+ }
714
+ DEBUG_FIXUPS(" lib_ordinal %u, weak_import %u, name_offset %u (%s), addend %llu\n",
715
+ imp.lib_ordinal, imp.weak_import, imp.name_offset, name, imp.addend);
716
+ d->plthook->entries[i].name = name;
717
+ d->plthook->entries[i].addr = (void**)(d->got_addr + i * sizeof(void*));
718
+ }
719
+
720
+ #ifdef PLTHOOK_DEBUG_FIXUPS
721
+ fp = fopen(image_name, "r");
722
+ if (fp == NULL) {
723
+ set_errmsg("failed to open file %s (error: %s)", image_name, strerror(errno));
724
+ rv = PLTHOOK_FILE_NOT_FOUND;
725
+ goto cleanup;
726
+ }
727
+
728
+ DEBUG_FIXUPS("dyld_chained_starts_in_image\n"
729
+ " seg_count %u\n",
730
+ starts->seg_count);
731
+ for (i = 0; i < starts->seg_count; i++) {
732
+ DEBUG_FIXUPS(" seg_info_offset[%u] %u\n",
733
+ i, starts->seg_info_offset[i]);
734
+ if (starts->seg_info_offset[i] == 0) {
735
+ continue;
736
+ }
737
+ const struct dyld_chained_starts_in_segment* seg = (const struct dyld_chained_starts_in_segment*)((char*)starts + starts->seg_info_offset[i]);
738
+ uint16_t j;
739
+ DEBUG_FIXUPS(" dyld_chained_starts_in_segment\n"
740
+ " size %u\n"
741
+ " page_size 0x%x\n"
742
+ " pointer_format %u\n"
743
+ " segment_offset %llu (0x%llx)\n"
744
+ " max_valid_pointer %u\n"
745
+ " page_count %u\n",
746
+ seg->size, seg->page_size, seg->pointer_format, seg->segment_offset, seg->segment_offset, seg->max_valid_pointer, seg->page_count);
747
+ for (j = 0; j < seg->page_count; j++) {
748
+ uint16_t index = j;
749
+ uint16_t break_loop = 1;
750
+ off_t offset;
751
+
752
+ if (seg->page_start[j] == DYLD_CHAINED_PTR_START_NONE) {
753
+ DEBUG_FIXUPS(" page_start[%u] DYLD_CHAINED_PTR_START_NONE\n", j);
754
+ continue;
755
+ }
756
+ if (seg->page_start[j] & DYLD_CHAINED_PTR_START_MULTI) {
757
+ index = seg->page_start[j] & ~DYLD_CHAINED_PTR_START_MULTI;
758
+ DEBUG_FIXUPS(" page_start[%u] (DYLD_CHAINED_PTR_START_MULTI | %u)\n", j, index);
759
+ break_loop = 0;
760
+ }
761
+ while (1) {
762
+ if (index != j) {
763
+ DEBUG_FIXUPS(" page_start[%u] %u\n", index, seg->page_start[index]);
764
+ }
765
+ offset = seg->segment_offset + j * seg->page_size + (seg->page_start[index] & ~DYLD_CHAINED_PTR_START_MULTI);
766
+ switch (seg->pointer_format) {
767
+ case DYLD_CHAINED_PTR_64_OFFSET: {
768
+ union {
769
+ struct dyld_chained_ptr_64_rebase rebase;
770
+ struct dyld_chained_ptr_64_bind bind;
771
+ } buf;
772
+
773
+ do {
774
+ if (fseeko(fp, offset, SEEK_SET) != 0) {
775
+ set_errmsg("failed to seek to %lld in %s", offset, image_name);
776
+ rv = PLTHOOK_INVALID_FILE_FORMAT;
777
+ goto cleanup;
778
+ }
779
+ if (fread(&buf, sizeof(buf), 1, fp) != 1) {
780
+ set_errmsg("failed to read fixup chain from %s", image_name);
781
+ rv = PLTHOOK_INVALID_FILE_FORMAT;
782
+ goto cleanup;
783
+ }
784
+ if (buf.rebase.bind) {
785
+ DEBUG_FIXUPS(" dyld_chained_ptr_64_bind\n"
786
+ " ordinal %d\n"
787
+ " addend %d\n"
788
+ " reserved %d\n"
789
+ " next %d\n"
790
+ " bind %d\n",
791
+ buf.bind.ordinal,
792
+ buf.bind.addend,
793
+ buf.bind.reserved,
794
+ buf.bind.next,
795
+ buf.bind.bind);
796
+ } else {
797
+ DEBUG_FIXUPS(" dyld_chained_ptr_64_rebase\n"
798
+ " target %llu\n"
799
+ " high8 %d\n"
800
+ " reserved %d\n"
801
+ " next %d\n"
802
+ " bind %d\n",
803
+ buf.rebase.target,
804
+ buf.rebase.high8,
805
+ buf.rebase.reserved,
806
+ buf.rebase.next,
807
+ buf.rebase.bind);
808
+ }
809
+ offset += buf.bind.next * 4;
810
+ } while (buf.bind.next != 0);
811
+ break;
812
+ }
813
+ case DYLD_CHAINED_PTR_ARM64E:
814
+ case DYLD_CHAINED_PTR_ARM64E_KERNEL:
815
+ case DYLD_CHAINED_PTR_ARM64E_USERLAND:
816
+ case DYLD_CHAINED_PTR_ARM64E_USERLAND24: {
817
+ // The following code isn't tested.
818
+ union {
819
+ struct dyld_chained_ptr_arm64e_rebase rebase;
820
+ struct dyld_chained_ptr_arm64e_bind bind;
821
+ struct dyld_chained_ptr_arm64e_bind24 bind24;
822
+ struct dyld_chained_ptr_arm64e_auth_rebase auth_rebase;
823
+ struct dyld_chained_ptr_arm64e_auth_bind auth_bind;
824
+ struct dyld_chained_ptr_arm64e_auth_bind24 auth_bind24;
825
+ } buf;
826
+
827
+ do {
828
+ if (fseeko(fp, offset, SEEK_SET) != 0) {
829
+ set_errmsg("failed to seek to %lld in %s", offset, image_name);
830
+ rv = PLTHOOK_INVALID_FILE_FORMAT;
831
+ goto cleanup;
832
+ }
833
+ if (fread(&buf, sizeof(buf), 1, fp) != 1) {
834
+ set_errmsg("failed to read fixup chain from %s", image_name);
835
+ rv = PLTHOOK_INVALID_FILE_FORMAT;
836
+ goto cleanup;
837
+ }
838
+ if (!buf.rebase.auth) {
839
+ if (!buf.rebase.bind) {
840
+ DEBUG_FIXUPS(" dyld_chained_ptr_arm64e_rebase\n"
841
+ " target %llu\n"
842
+ " high8 %d\n"
843
+ " next %d\n"
844
+ " bind %d\n" // == 0
845
+ " auth %d\n", // == 0
846
+ buf.rebase.target,
847
+ buf.rebase.high8,
848
+ buf.rebase.next,
849
+ buf.rebase.bind,
850
+ buf.rebase.auth);
851
+ } else if (seg->pointer_format != DYLD_CHAINED_PTR_ARM64E_USERLAND24) {
852
+ DEBUG_FIXUPS(" dyld_chained_ptr_arm64e_bind\n"
853
+ " ordinal %d\n"
854
+ " zero %d\n"
855
+ " addend %d\n"
856
+ " next %d\n"
857
+ " bind %d\n" // == 1
858
+ " auth %d\n", // == 0
859
+ buf.bind.ordinal,
860
+ buf.bind.zero,
861
+ buf.bind.addend,
862
+ buf.bind.next,
863
+ buf.bind.bind,
864
+ buf.bind.auth);
865
+ } else {
866
+ DEBUG_FIXUPS(" dyld_chained_ptr_arm64e_bind24\n"
867
+ " ordinal %d\n"
868
+ " zero %d\n"
869
+ " addend %d\n"
870
+ " next %d\n"
871
+ " bind %d\n" // == 1
872
+ " auth %d\n", // == 0
873
+ buf.bind24.ordinal,
874
+ buf.bind24.zero,
875
+ buf.bind24.addend,
876
+ buf.bind24.next,
877
+ buf.bind24.bind,
878
+ buf.bind24.auth);
879
+ }
880
+ } else {
881
+ if (!buf.rebase.bind) {
882
+ DEBUG_FIXUPS(" dyld_chained_ptr_arm64e_auth_rebase\n"
883
+ " target %u\n"
884
+ " diversity %d\n"
885
+ " addrDiv %d\n"
886
+ " key %d\n"
887
+ " next %d\n"
888
+ " bind %d\n" // == 0
889
+ " auth %d\n", // == 1
890
+ buf.auth_rebase.target,
891
+ buf.auth_rebase.diversity,
892
+ buf.auth_rebase.addrDiv,
893
+ buf.auth_rebase.key,
894
+ buf.auth_rebase.next,
895
+ buf.auth_rebase.bind,
896
+ buf.auth_rebase.auth);
897
+ } else if (seg->pointer_format != DYLD_CHAINED_PTR_ARM64E_USERLAND24) {
898
+ DEBUG_FIXUPS(" dyld_chained_ptr_arm64e_auth_bind\n"
899
+ " ordinal %d\n"
900
+ " zero %d\n"
901
+ " diversity %d\n"
902
+ " addrDiv %d\n"
903
+ " key %d\n"
904
+ " next %d\n"
905
+ " bind %d\n" // == 1
906
+ " auth %d\n", // == 1
907
+ buf.auth_bind.ordinal,
908
+ buf.auth_bind.zero,
909
+ buf.auth_bind.diversity,
910
+ buf.auth_bind.addrDiv,
911
+ buf.auth_bind.key,
912
+ buf.auth_bind.next,
913
+ buf.auth_bind.bind,
914
+ buf.auth_bind.auth);
915
+ } else {
916
+ DEBUG_FIXUPS(" dyld_chained_ptr_arm64e_auth_bind24\n"
917
+ " ordinal %d\n"
918
+ " zero %d\n"
919
+ " diversity %d\n"
920
+ " addrDiv %d\n"
921
+ " key %d\n"
922
+ " next %d\n"
923
+ " bind %d\n" // == 1
924
+ " auth %d\n", // == 1
925
+ buf.auth_bind24.ordinal,
926
+ buf.auth_bind24.zero,
927
+ buf.auth_bind24.diversity,
928
+ buf.auth_bind24.addrDiv,
929
+ buf.auth_bind24.key,
930
+ buf.auth_bind24.next,
931
+ buf.auth_bind24.bind,
932
+ buf.auth_bind24.auth);
933
+ }
934
+ }
935
+ if (seg->pointer_format == DYLD_CHAINED_PTR_ARM64E_KERNEL) {
936
+ offset += buf.rebase.next * 4;
937
+ } else {
938
+ offset += buf.rebase.next * 8;
939
+ }
940
+ } while (buf.rebase.next != 0);
941
+ break;
942
+ }
943
+ default:
944
+ DEBUG_FIXUPS("unsupported pointer_format: %u\n", seg->pointer_format);
945
+ break_loop = 1;
946
+ break;
947
+ }
948
+ if (break_loop) {
949
+ break;
950
+ }
951
+ break_loop = seg->page_start[++index] & DYLD_CHAINED_PTR_START_MULTI;
952
+ } // while (1) */
953
+ }
954
+ }
955
+ #endif
956
+ rv = 0;
957
+ cleanup:
958
+ #ifdef PLTHOOK_DEBUG_FIXUPS
959
+ if (fp != NULL) {
960
+ fclose(fp);
961
+ }
962
+ #endif
963
+ if (rv != 0 && d->plthook) {
964
+ free(d->plthook);
965
+ d->plthook = NULL;
966
+ }
967
+ return rv;
968
+ }
969
+
423
970
  int plthook_enum(plthook_t *plthook, unsigned int *pos, const char **name_out, void ***addr_out)
424
971
  {
425
972
  if (*pos >= plthook->num_entries) {
@@ -473,7 +1020,18 @@ matched:
473
1020
  if (oldfunc) {
474
1021
  *oldfunc = *addr;
475
1022
  }
476
- *addr = funcaddr;
1023
+ if (plthook->readonly_segment) {
1024
+ size_t page_size = sysconf(_SC_PAGESIZE);
1025
+ void *base = (void*)((size_t)addr & ~(page_size - 1));
1026
+ if (mprotect(base, page_size, PROT_READ | PROT_WRITE) != 0) {
1027
+ set_errmsg("Cannot change memory protection at address %p", base);
1028
+ return PLTHOOK_INTERNAL_ERROR;
1029
+ }
1030
+ *addr = funcaddr;
1031
+ mprotect(base, page_size, PROT_READ);
1032
+ } else {
1033
+ *addr = funcaddr;
1034
+ }
477
1035
  return 0;
478
1036
  }
479
1037
  if (rv == EOF) {