ruby-oci8 2.2.11 → 2.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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) {