ruby-oci8 2.2.12 → 2.2.14

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