ruby-oci8 2.2.12 → 2.2.14

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,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) {