ruby-oci8 2.1.7 → 2.1.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,474 @@
1
+ /* -*- indent-tabs-mode: nil -*-
2
+ *
3
+ * plthook_osx.c -- implemention of plthook for OS X
4
+ *
5
+ * URL: https://github.com/kubo/plthook
6
+ *
7
+ * ------------------------------------------------------
8
+ *
9
+ * Copyright 2014 Kubo Takehiro <kubo@jiubao.org>
10
+ *
11
+ * Redistribution and use in source and binary forms, with or without modification, are
12
+ * permitted provided that the following conditions are met:
13
+ *
14
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
15
+ * conditions and the following disclaimer.
16
+ *
17
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
18
+ * of conditions and the following disclaimer in the documentation and/or other materials
19
+ * provided with the distribution.
20
+ *
21
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR IMPLIED
22
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
23
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
24
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+ *
31
+ * The views and conclusions contained in the software and documentation are those of the
32
+ * authors and should not be interpreted as representing official policies, either expressed
33
+ * or implied, of the authors.
34
+ *
35
+ */
36
+ #include <stdio.h>
37
+ #include <stdarg.h>
38
+ #include <stdlib.h>
39
+ #include <string.h>
40
+ #include <dlfcn.h>
41
+ #include <mach-o/dyld.h>
42
+ #include "plthook.h"
43
+
44
+ // #define PLTHOOK_DEBUG_CMD 1
45
+ // #define PLTHOOK_DEBUG_BIND 1
46
+
47
+ #ifdef PLTHOOK_DEBUG_CMD
48
+ #define DEBUG_CMD(...) fprintf(stderr, __VA_ARGS__)
49
+ #else
50
+ #define DEBUG_CMD(...)
51
+ #endif
52
+
53
+ #ifdef PLTHOOK_DEBUG_BIND
54
+ #define DEBUG_BIND(...) fprintf(stderr, __VA_ARGS__)
55
+ #else
56
+ #define DEBUG_BIND(...)
57
+ #endif
58
+
59
+ #ifdef __LP64__
60
+ #define segment_command_ segment_command_64
61
+ #else
62
+ #define segment_command_ segment_command
63
+ #endif
64
+
65
+ typedef struct {
66
+ const char *name;
67
+ void **addr;
68
+ } bind_address_t;
69
+
70
+ struct plthook {
71
+ unsigned int num_entries;
72
+ bind_address_t entries[1];
73
+ };
74
+
75
+ static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *mh);
76
+ static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint32_t lazy_bind_off, uint32_t lazy_bind_size, struct segment_command_ **segments, int addrdiff);
77
+
78
+ static void set_errmsg(const char *fmt, ...) __attribute__((__format__ (__printf__, 1, 2)));
79
+ static void set_bind_addr(unsigned int *idx, plthook_t *plthook, const uint8_t *base, const char *sym_name, int seg_index, int seg_offset, struct segment_command_ **segments);
80
+
81
+ static uint64_t uleb128(const uint8_t **p)
82
+ {
83
+ uint64_t r = 0;
84
+ int s = 0;
85
+ do {
86
+ r |= (uint64_t)(**p & 0x7f) << s;
87
+ s += 7;
88
+ } while (*(*p)++ >= 0x80);
89
+ return r;
90
+ }
91
+
92
+ static int64_t sleb128(const uint8_t** p)
93
+ {
94
+ int64_t r = 0;
95
+ int s = 0;
96
+ for (;;) {
97
+ uint8_t b = *(*p)++;
98
+ if (b < 0x80) {
99
+ if (b & 0x40) {
100
+ r -= (0x80 - b) << s;
101
+ } else {
102
+ r |= (b & 0x3f) << s;
103
+ }
104
+ break;
105
+ }
106
+ r |= (b & 0x7f) << s;
107
+ s += 7;
108
+ }
109
+ return r;
110
+ }
111
+
112
+ static char errmsg[512];
113
+
114
+ int plthook_open(plthook_t **plthook_out, const char *filename)
115
+ {
116
+ uint32_t idx = 0;
117
+
118
+ if (filename != NULL) {
119
+ size_t namelen = strlen(filename);
120
+
121
+ while (1) {
122
+ const char *image_name = _dyld_get_image_name(idx);
123
+ size_t offset = 0;
124
+
125
+ if (image_name == NULL) {
126
+ *plthook_out = NULL;
127
+ set_errmsg("Cannot find file: %s", filename);
128
+ return PLTHOOK_FILE_NOT_FOUND;
129
+ }
130
+ if (*filename != '/') {
131
+ size_t image_name_len = strlen(image_name);
132
+ if (image_name_len > namelen) {
133
+ offset = image_name_len - namelen;
134
+ }
135
+ }
136
+ if (strcmp(image_name + offset, filename) == 0) {
137
+ break;
138
+ }
139
+ idx++;
140
+ }
141
+ }
142
+ return plthook_open_real(plthook_out, _dyld_get_image_header(idx));
143
+ }
144
+
145
+ int plthook_open_by_address(plthook_t **plthook_out, void *address)
146
+ {
147
+ Dl_info dlinfo;
148
+
149
+ if (!dladdr(address, &dlinfo)) {
150
+ *plthook_out = NULL;
151
+ set_errmsg("Cannot find address: %p", address);
152
+ return PLTHOOK_FILE_NOT_FOUND;
153
+ }
154
+ return plthook_open_real(plthook_out, dlinfo.dli_fbase);
155
+ }
156
+
157
+ #define NUM_SEGMENTS 10
158
+
159
+ static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *mh)
160
+ {
161
+ struct load_command *cmd;
162
+ const uint8_t *base = (const uint8_t *)mh;
163
+ uint32_t lazy_bind_off = 0;
164
+ uint32_t lazy_bind_size = 0;
165
+ struct segment_command_ *segments[NUM_SEGMENTS];
166
+ int segment_idx = 0;
167
+ unsigned int nbind;
168
+ int addrdiff = 0;
169
+ int i;
170
+
171
+ memset(segments, 0, sizeof(segments));
172
+ #ifdef __LP64__
173
+ cmd = (struct load_command *)((size_t)mh + sizeof(struct mach_header_64));
174
+ #else
175
+ cmd = (struct load_command *)((size_t)mh + sizeof(struct mach_header));
176
+ #endif
177
+ for (i = 0; i < mh->ncmds; i++) {
178
+ struct dyld_info_command *dyld_info;
179
+ struct segment_command *segment;
180
+ struct segment_command_64 *segment64;
181
+
182
+ switch (cmd->cmd) {
183
+ case LC_SEGMENT: /* 0x1 */
184
+ segment = (struct segment_command *)cmd;
185
+ DEBUG_CMD("LC_SEGMENT\n"
186
+ " segname %s\n"
187
+ " vmaddr %8x vmsize %8x\n"
188
+ " fileoff %8x filesize %8x\n"
189
+ " maxprot %8x initprot %8x\n"
190
+ " nsects %8d flags %8x\n",
191
+ segment->segname,
192
+ segment->vmaddr, segment->vmsize,
193
+ segment->fileoff, segment->filesize,
194
+ segment->maxprot, segment->initprot,
195
+ segment->nsects, segment->flags);
196
+ if (strcmp(segment->segname, "__LINKEDIT") == 0) {
197
+ addrdiff = segment->vmaddr - segment->fileoff;
198
+ }
199
+ #ifndef __LP64__
200
+ segments[segment_idx++] = segment;
201
+ #endif
202
+ break;
203
+ case LC_SEGMENT_64: /* 0x19 */
204
+ segment64 = (struct segment_command_64 *)cmd;
205
+ DEBUG_CMD("LC_SEGMENT_64\n"
206
+ " segname %s\n"
207
+ " vmaddr %8llx vmsize %8llx\n"
208
+ " fileoff %8llx filesize %8llx\n"
209
+ " maxprot %8x initprot %8x\n"
210
+ " nsects %8d flags %8x\n",
211
+ segment64->segname,
212
+ segment64->vmaddr, segment64->vmsize,
213
+ segment64->fileoff, segment64->filesize,
214
+ segment64->maxprot, segment64->initprot,
215
+ segment64->nsects, segment64->flags);
216
+ if (strcmp(segment64->segname, "__LINKEDIT") == 0) {
217
+ addrdiff = segment64->vmaddr - segment64->fileoff;
218
+ }
219
+ #ifdef __LP64__
220
+ segments[segment_idx++] = segment64;
221
+ #endif
222
+ break;
223
+ case LC_DYLD_INFO_ONLY: /* (0x22|LC_REQ_DYLD) */
224
+ dyld_info= (struct dyld_info_command *)cmd;
225
+ lazy_bind_off = dyld_info->lazy_bind_off;
226
+ lazy_bind_size = dyld_info->lazy_bind_size;
227
+ DEBUG_CMD("LC_DYLD_INFO_ONLY\n"
228
+ " offset size\n"
229
+ " rebase %8x %8x\n"
230
+ " bind %8x %8x\n"
231
+ " weak_bind %8x %8x\n"
232
+ " lazy_bind %8x %8x\n"
233
+ " export_bind %8x %8x\n",
234
+ dyld_info->rebase_off, dyld_info->rebase_size,
235
+ dyld_info->bind_off, dyld_info->bind_size,
236
+ dyld_info->weak_bind_off, dyld_info->weak_bind_size,
237
+ dyld_info->lazy_bind_off, dyld_info->lazy_bind_size,
238
+ dyld_info->export_off, dyld_info->export_size);
239
+ break;
240
+ case LC_SYMTAB: /* 0x2 */
241
+ DEBUG_CMD("LC_SYMTAB\n");
242
+ break;
243
+ case LC_DYSYMTAB: /* 0xb */
244
+ DEBUG_CMD("LC_DYSYMTAB\n");
245
+ break;
246
+ case LC_LOAD_DYLIB: /* 0xc */
247
+ DEBUG_CMD("LC_LOAD_DYLIB\n");
248
+ break;
249
+ case LC_ID_DYLIB: /* 0xd */
250
+ DEBUG_CMD("LC_ID_DYLIB\n");
251
+ break;
252
+ case LC_LOAD_DYLINKER: /* 0xe */
253
+ DEBUG_CMD("LC_LOAD_DYLINKER\n");
254
+ break;
255
+ case LC_ROUTINES_64: /* 0x1a */
256
+ DEBUG_CMD("LC_ROUTINES_64\n");
257
+ break;
258
+ case LC_UUID: /* 0x1b */
259
+ DEBUG_CMD("LC_UUID\n");
260
+ break;
261
+ case LC_VERSION_MIN_MACOSX: /* 0x24 */
262
+ DEBUG_CMD("LC_VERSION_MIN_MACOSX\n");
263
+ break;
264
+ case LC_FUNCTION_STARTS: /* 0x26 */
265
+ DEBUG_CMD("LC_FUNCTION_STARTS\n");
266
+ break;
267
+ case LC_MAIN: /* 0x28|LC_REQ_DYLD */
268
+ DEBUG_CMD("LC_MAIN\n");
269
+ break;
270
+ case LC_DATA_IN_CODE: /* 0x29 */
271
+ DEBUG_CMD("LC_DATA_IN_CODE\n");
272
+ break;
273
+ case LC_SOURCE_VERSION: /* 0x2A */
274
+ DEBUG_CMD("LC_SOURCE_VERSION\n");
275
+ break;
276
+ case LC_DYLIB_CODE_SIGN_DRS: /* 0x2B */
277
+ DEBUG_CMD("LC_DYLIB_CODE_SIGN_DRS\n");
278
+ break;
279
+ default:
280
+ DEBUG_CMD("LC_? (0x%x)\n", cmd->cmd);
281
+ }
282
+ cmd = (struct load_command *)((size_t)cmd + cmd->cmdsize);
283
+ }
284
+ nbind = get_bind_addr(NULL, base, lazy_bind_off, lazy_bind_size, segments, addrdiff);
285
+ *plthook_out = (plthook_t*)malloc(offsetof(plthook_t, entries) + sizeof(bind_address_t) * nbind);
286
+ (*plthook_out)->num_entries = nbind;
287
+ get_bind_addr(*plthook_out, base, lazy_bind_off, lazy_bind_size, segments, addrdiff);
288
+
289
+ return 0;
290
+ }
291
+
292
+ static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint32_t lazy_bind_off, uint32_t lazy_bind_size, struct segment_command_ **segments, int addrdiff)
293
+ {
294
+ const uint8_t *ptr = base + lazy_bind_off + addrdiff;
295
+ const uint8_t *end = ptr + lazy_bind_size;
296
+ const char *sym_name;
297
+ int seg_index = 0;
298
+ uint64_t seg_offset = 0;
299
+ int count, skip;
300
+ unsigned int idx;
301
+ DEBUG_BIND("get_bind_addr(%p, 0x%x, 0x%x", base, lazy_bind_off, lazy_bind_size);
302
+ for (idx = 0; segments[idx] != NULL; idx++) {
303
+ DEBUG_BIND(", [%s]", segments[idx]->segname);
304
+ }
305
+ DEBUG_BIND(")\n");
306
+
307
+ idx = 0;
308
+ while (ptr < end) {
309
+ uint8_t op = *ptr & BIND_OPCODE_MASK;
310
+ uint8_t imm = *ptr & BIND_IMMEDIATE_MASK;
311
+ uint64_t ulebval;
312
+ int64_t slebval;
313
+ int i;
314
+
315
+ DEBUG_BIND("0x%02x: ", *ptr);
316
+ ptr++;
317
+ switch (op) {
318
+ case BIND_OPCODE_DONE:
319
+ DEBUG_BIND("BIND_OPCODE_DONE\n");
320
+ break;
321
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
322
+ DEBUG_BIND("BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: ordinal = %u\n", imm);
323
+ break;
324
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
325
+ ulebval = uleb128(&ptr);
326
+ DEBUG_BIND("BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: ordinal = %llu\n", ulebval);
327
+ break;
328
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
329
+ if (imm == 0) {
330
+ DEBUG_BIND("BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: ordinal = 0\n");
331
+ } else {
332
+ DEBUG_BIND("BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: ordinal = %u\n", BIND_OPCODE_MASK | imm);
333
+ }
334
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
335
+ sym_name = (const char*)ptr;
336
+ ptr += strlen(sym_name) + 1;
337
+ DEBUG_BIND("BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: sym_name = %s\n", sym_name);
338
+ break;
339
+ case BIND_OPCODE_SET_TYPE_IMM:
340
+ DEBUG_BIND("BIND_OPCODE_SET_TYPE_IMM: type = %u\n", imm);
341
+ break;
342
+ case BIND_OPCODE_SET_ADDEND_SLEB:
343
+ slebval = sleb128(&ptr);
344
+ DEBUG_BIND("BIND_OPCODE_SET_ADDEND_SLEB: ordinal = %lld\n", slebval);
345
+ break;
346
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
347
+ seg_index = imm;
348
+ seg_offset = uleb128(&ptr);
349
+ DEBUG_BIND("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: seg_index = %u, seg_offset = 0x%llx\n", seg_index, seg_offset);
350
+ break;
351
+ case BIND_OPCODE_ADD_ADDR_ULEB:
352
+ seg_offset += uleb128(&ptr);
353
+ DEBUG_BIND("BIND_OPCODE_ADD_ADDR_ULEB: seg_offset = 0x%llx\n", seg_offset);
354
+ break;
355
+ case BIND_OPCODE_DO_BIND:
356
+ set_bind_addr(&idx, plthook, base, sym_name, seg_index, seg_offset, segments);
357
+ DEBUG_BIND("BIND_OPCODE_DO_BIND\n");
358
+ break;
359
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
360
+ seg_offset += uleb128(&ptr);
361
+ DEBUG_BIND("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: seg_offset = 0x%llx\n", seg_offset);
362
+ break;
363
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
364
+ set_bind_addr(&idx, plthook, base, sym_name, seg_index, seg_offset, segments);
365
+ seg_offset += imm * sizeof(void *);
366
+ DEBUG_BIND("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED\n");
367
+ break;
368
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
369
+ count = uleb128(&ptr);
370
+ skip = uleb128(&ptr);
371
+ for (i = 0; i < count; i++) {
372
+ set_bind_addr(&idx, plthook, base, sym_name, seg_index, seg_offset, segments);
373
+ seg_offset += skip;
374
+ }
375
+ DEBUG_BIND("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB\n");
376
+ break;
377
+ }
378
+ }
379
+ return idx;
380
+ }
381
+
382
+ static void set_bind_addr(unsigned int *idx, plthook_t *plthook, const uint8_t *base, const char *sym_name, int seg_index, int seg_offset, struct segment_command_ **segments)
383
+ {
384
+ if (plthook != NULL) {
385
+ uint32_t vmaddr = segments[seg_index]->vmaddr;
386
+ plthook->entries[*idx].name = sym_name;
387
+ plthook->entries[*idx].addr = (void**)(base + vmaddr + seg_offset);
388
+ }
389
+ (*idx)++;
390
+ }
391
+
392
+ int plthook_enum(plthook_t *plthook, unsigned int *pos, const char **name_out, void ***addr_out)
393
+ {
394
+ if (*pos >= plthook->num_entries) {
395
+ *name_out = NULL;
396
+ *addr_out = NULL;
397
+ return EOF;
398
+ }
399
+ *name_out = plthook->entries[*pos].name;
400
+ *addr_out = plthook->entries[*pos].addr;
401
+ (*pos)++;
402
+ return 0;
403
+ }
404
+
405
+ int plthook_replace(plthook_t *plthook, const char *funcname, void *funcaddr, void **oldfunc)
406
+ {
407
+ size_t funcnamelen = strlen(funcname);
408
+ unsigned int pos = 0;
409
+ const char *name;
410
+ void **addr;
411
+ int rv;
412
+
413
+ if (plthook == NULL) {
414
+ set_errmsg("invalid argument: The first argument is null.");
415
+ return PLTHOOK_INVALID_ARGUMENT;
416
+ }
417
+ while ((rv = plthook_enum(plthook, &pos, &name, &addr)) == 0) {
418
+ if (strncmp(name, funcname, funcnamelen) == 0) {
419
+ if (name[funcnamelen] == '\0' || name[funcnamelen] == '$') {
420
+ goto matched;
421
+ }
422
+ }
423
+ if (name[0] == '@') {
424
+ /* Oracle libclntsh.dylib imports 'read' as '@_read'. */
425
+ name++;
426
+ if (strncmp(name, funcname, funcnamelen) == 0) {
427
+ if (name[funcnamelen] == '\0' || name[funcnamelen] == '$') {
428
+ goto matched;
429
+ }
430
+ }
431
+ }
432
+ if (name[0] == '_') {
433
+ name++;
434
+ if (strncmp(name, funcname, funcnamelen) == 0) {
435
+ if (name[funcnamelen] == '\0' || name[funcnamelen] == '$') {
436
+ goto matched;
437
+ }
438
+ }
439
+ }
440
+ continue;
441
+ matched:
442
+ if (oldfunc) {
443
+ *oldfunc = *addr;
444
+ }
445
+ *addr = funcaddr;
446
+ return 0;
447
+ }
448
+ if (rv == EOF) {
449
+ set_errmsg("no such function: %s", funcname);
450
+ rv = PLTHOOK_FUNCTION_NOT_FOUND;
451
+ }
452
+ return rv;
453
+ }
454
+
455
+ void plthook_close(plthook_t *plthook)
456
+ {
457
+ if (plthook != NULL) {
458
+ free(plthook);
459
+ }
460
+ return;
461
+ }
462
+
463
+ const char *plthook_error(void)
464
+ {
465
+ return errmsg;
466
+ }
467
+
468
+ static void set_errmsg(const char *fmt, ...)
469
+ {
470
+ va_list ap;
471
+ va_start(ap, fmt);
472
+ vsnprintf(errmsg, sizeof(errmsg) - 1, fmt, ap);
473
+ va_end(ap);
474
+ }