Skip to content

Commit 8fbdd8d

Browse files
committed
Fix SEGV when plthook_open(..., "/usr/lib/libc.dylib") on macOS.
(close #19) Use _dyld_get_image_vmaddr_slide() + segment.vmaddr instead of base + segment.vmaddr.
1 parent e17f730 commit 8fbdd8d

File tree

1 file changed

+113
-68
lines changed

1 file changed

+113
-68
lines changed

plthook_osx.c

+113-68
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*
77
* ------------------------------------------------------
88
*
9-
* Copyright 2014 Kubo Takehiro <kubo@jiubao.org>
9+
* Copyright 2014-2019 Kubo Takehiro <kubo@jiubao.org>
1010
*
1111
* Redistribution and use in source and binary forms, with or without modification, are
1212
* permitted provided that the following conditions are met:
@@ -37,6 +37,7 @@
3737
#include <stdarg.h>
3838
#include <stdlib.h>
3939
#include <string.h>
40+
#include <inttypes.h>
4041
#include <dlfcn.h>
4142
#include <mach-o/dyld.h>
4243
#include "plthook.h"
@@ -72,11 +73,21 @@ struct plthook {
7273
bind_address_t entries[1];
7374
};
7475

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);
76+
#define MAX_SEGMENTS 8
77+
78+
typedef struct {
79+
plthook_t *plthook;
80+
intptr_t slide;
81+
int num_segments;
82+
int linkedit_segment_idx;
83+
struct segment_command_ *segments[MAX_SEGMENTS];
84+
} data_t;
85+
86+
static int plthook_open_real(plthook_t **plthook_out, uint32_t image_idx, const struct mach_header *mh, const char *image_name);
87+
static unsigned int set_bind_addrs(data_t *d, uint32_t lazy_bind_off, uint32_t lazy_bind_size);
88+
static void set_bind_addr(data_t *d, unsigned int *idx, const char *sym_name, int seg_index, int seg_offset);
7789

7890
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);
8091

8192
static uint64_t uleb128(const uint8_t **p)
8293
{
@@ -113,33 +124,40 @@ static char errmsg[512];
113124

114125
int plthook_open(plthook_t **plthook_out, const char *filename)
115126
{
116-
uint32_t idx = 0;
127+
size_t namelen;
128+
uint32_t cnt;
129+
uint32_t idx;
117130

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;
131+
if (filename == NULL) {
132+
return plthook_open_real(plthook_out, 0, NULL, NULL);
133+
}
134+
cnt = _dyld_image_count();
135+
namelen = strlen(filename);
136+
namelen = strlen(filename);
137+
cnt = _dyld_image_count();
138+
139+
for (idx = 0; idx < cnt; idx++) {
140+
const char *image_name = _dyld_get_image_name(idx);
141+
size_t offset = 0;
142+
143+
if (image_name == NULL) {
144+
*plthook_out = NULL;
145+
set_errmsg("Cannot find file at image index %u", idx);
146+
return PLTHOOK_INTERNAL_ERROR;
147+
}
148+
if (*filename != '/') {
149+
size_t image_name_len = strlen(image_name);
150+
if (image_name_len > namelen) {
151+
offset = image_name_len - namelen;
138152
}
139-
idx++;
153+
}
154+
if (strcmp(image_name + offset, filename) == 0) {
155+
return plthook_open_real(plthook_out, idx, NULL, image_name);
140156
}
141157
}
142-
return plthook_open_real(plthook_out, _dyld_get_image_header(idx));
158+
*plthook_out = NULL;
159+
set_errmsg("Cannot find file: %s", filename);
160+
return PLTHOOK_FILE_NOT_FOUND;
143161
}
144162

145163
int plthook_open_by_handle(plthook_t **plthook_out, void *hndl)
@@ -149,25 +167,26 @@ int plthook_open_by_handle(plthook_t **plthook_out, void *hndl)
149167
RTLD_LAZY | RTLD_NOLOAD | RTLD_FIRST,
150168
};
151169
int flag_idx;
170+
uint32_t cnt = _dyld_image_count();
152171
#define NUM_FLAGS (sizeof(flags) / sizeof(flags[0]))
153172

154173
if (hndl == NULL) {
155174
set_errmsg("NULL handle");
156175
return PLTHOOK_FILE_NOT_FOUND;
157176
}
158177
for (flag_idx = 0; flag_idx < NUM_FLAGS; flag_idx++) {
159-
const char *image_name = NULL;
160-
uint32_t idx = 0;
178+
uint32_t idx;
161179

162-
do {
180+
for (idx = 0; idx < cnt; idx++) {
181+
const char *image_name = idx ? _dyld_get_image_name(idx) : NULL;
163182
void *handle = dlopen(image_name, flags[flag_idx]);
164183
if (handle != NULL) {
165184
dlclose(handle);
166185
if (handle == hndl) {
167-
return plthook_open_real(plthook_out, _dyld_get_image_header(idx));
186+
return plthook_open_real(plthook_out, idx, NULL, image_name);
168187
}
169188
}
170-
} while ((image_name = _dyld_get_image_name(++idx)) != NULL);
189+
}
171190
}
172191
set_errmsg("Cannot find the image correspond to handle %p", hndl);
173192
return PLTHOOK_FILE_NOT_FOUND;
@@ -176,30 +195,43 @@ int plthook_open_by_handle(plthook_t **plthook_out, void *hndl)
176195
int plthook_open_by_address(plthook_t **plthook_out, void *address)
177196
{
178197
Dl_info dlinfo;
198+
uint32_t idx = 0;
199+
uint32_t cnt = _dyld_image_count();
179200

180201
if (!dladdr(address, &dlinfo)) {
181202
*plthook_out = NULL;
182203
set_errmsg("Cannot find address: %p", address);
183204
return PLTHOOK_FILE_NOT_FOUND;
184205
}
185-
return plthook_open_real(plthook_out, dlinfo.dli_fbase);
206+
for (idx = 0; idx < cnt; idx++) {
207+
if (dlinfo.dli_fbase == _dyld_get_image_header(idx)) {
208+
return plthook_open_real(plthook_out, idx, dlinfo.dli_fbase, dlinfo.dli_fname);
209+
}
210+
}
211+
set_errmsg("Cannot find the image index for base address: %p", dlinfo.dli_fbase);
212+
return PLTHOOK_FILE_NOT_FOUND;
186213
}
187214

188-
#define NUM_SEGMENTS 10
189-
190-
static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *mh)
215+
static int plthook_open_real(plthook_t **plthook_out, uint32_t image_idx, const struct mach_header *mh, const char *image_name)
191216
{
192217
struct load_command *cmd;
193-
const uint8_t *base = (const uint8_t *)mh;
194218
uint32_t lazy_bind_off = 0;
195219
uint32_t lazy_bind_size = 0;
196-
struct segment_command_ *segments[NUM_SEGMENTS];
197-
int segment_idx = 0;
198220
unsigned int nbind;
199-
int addrdiff = 0;
221+
data_t data = {NULL,};
222+
size_t size;
200223
int i;
201224

202-
memset(segments, 0, sizeof(segments));
225+
data.linkedit_segment_idx = -1;
226+
data.slide = _dyld_get_image_vmaddr_slide(image_idx);
227+
DEBUG_CMD("slide=%"PRIxPTR"\n", data.slide);
228+
if (mh == NULL) {
229+
mh = _dyld_get_image_header(image_idx);
230+
}
231+
if (image_name == NULL) {
232+
image_name = _dyld_get_image_name(image_idx);
233+
}
234+
203235
#ifdef __LP64__
204236
cmd = (struct load_command *)((size_t)mh + sizeof(struct mach_header_64));
205237
#else
@@ -224,11 +256,15 @@ static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *
224256
segment->fileoff, segment->filesize,
225257
segment->maxprot, segment->initprot,
226258
segment->nsects, segment->flags);
259+
#ifndef __LP64__
227260
if (strcmp(segment->segname, "__LINKEDIT") == 0) {
228-
addrdiff = segment->vmaddr - segment->fileoff;
261+
data.linkedit_segment_idx = data.num_segments;
229262
}
230-
#ifndef __LP64__
231-
segments[segment_idx++] = segment;
263+
if (data.num_segments == MAX_SEGMENTS) {
264+
set_errmsg("Too many segments: %s", image_name);
265+
return PLTHOOK_INTERNAL_ERROR;
266+
}
267+
data.segments[data.num_segments++] = segment;
232268
#endif
233269
break;
234270
case LC_SEGMENT_64: /* 0x19 */
@@ -244,11 +280,15 @@ static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *
244280
segment64->fileoff, segment64->filesize,
245281
segment64->maxprot, segment64->initprot,
246282
segment64->nsects, segment64->flags);
283+
#ifdef __LP64__
247284
if (strcmp(segment64->segname, "__LINKEDIT") == 0) {
248-
addrdiff = segment64->vmaddr - segment64->fileoff;
285+
data.linkedit_segment_idx = data.num_segments;
249286
}
250-
#ifdef __LP64__
251-
segments[segment_idx++] = segment64;
287+
if (data.num_segments == MAX_SEGMENTS) {
288+
set_errmsg("Too many segments: %s", image_name);
289+
return PLTHOOK_INTERNAL_ERROR;
290+
}
291+
data.segments[data.num_segments++] = segment64;
252292
#endif
253293
break;
254294
case LC_DYLD_INFO_ONLY: /* (0x22|LC_REQ_DYLD) */
@@ -312,30 +352,35 @@ static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *
312352
}
313353
cmd = (struct load_command *)((size_t)cmd + cmd->cmdsize);
314354
}
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);
355+
if (data.linkedit_segment_idx == -1) {
356+
set_errmsg("Cannot find the linkedit segment: %s", image_name);
357+
return PLTHOOK_INVALID_FILE_FORMAT;
358+
}
359+
nbind = set_bind_addrs(&data, lazy_bind_off, lazy_bind_size);
360+
size = offsetof(plthook_t, entries) + sizeof(bind_address_t) * nbind;
361+
data.plthook = (plthook_t*)malloc(size);
362+
if (data.plthook == NULL) {
363+
set_errmsg("failed to allocate memory: %" PRIuPTR " bytes", size);
364+
return PLTHOOK_OUT_OF_MEMORY;
365+
}
366+
data.plthook->num_entries = nbind;
367+
set_bind_addrs(&data, lazy_bind_off, lazy_bind_size);
319368

369+
*plthook_out = data.plthook;
320370
return 0;
321371
}
322372

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, int addrdiff)
373+
static unsigned int set_bind_addrs(data_t *data, uint32_t lazy_bind_off, uint32_t lazy_bind_size)
324374
{
325-
const uint8_t *ptr = base + lazy_bind_off + addrdiff;
375+
struct segment_command_ *linkedit = data->segments[data->linkedit_segment_idx];
376+
const uint8_t *ptr = (uint8_t*)(linkedit->vmaddr - linkedit->fileoff + data->slide + lazy_bind_off);
326377
const uint8_t *end = ptr + lazy_bind_size;
327378
const char *sym_name;
328379
int seg_index = 0;
329380
uint64_t seg_offset = 0;
330381
int 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");
382+
unsigned int idx = 0;
337383

338-
idx = 0;
339384
while (ptr < end) {
340385
uint8_t op = *ptr & BIND_OPCODE_MASK;
341386
uint8_t imm = *ptr & BIND_IMMEDIATE_MASK;
@@ -384,23 +429,23 @@ static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint3
384429
DEBUG_BIND("BIND_OPCODE_ADD_ADDR_ULEB: seg_offset = 0x%llx\n", seg_offset);
385430
break;
386431
case BIND_OPCODE_DO_BIND:
387-
set_bind_addr(&idx, plthook, base, sym_name, seg_index, seg_offset, segments);
432+
set_bind_addr(data, &idx, sym_name, seg_index, seg_offset);
388433
DEBUG_BIND("BIND_OPCODE_DO_BIND\n");
389434
break;
390435
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
391436
seg_offset += uleb128(&ptr);
392437
DEBUG_BIND("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: seg_offset = 0x%llx\n", seg_offset);
393438
break;
394439
case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
395-
set_bind_addr(&idx, plthook, base, sym_name, seg_index, seg_offset, segments);
440+
set_bind_addr(data, &idx, sym_name, seg_index, seg_offset);
396441
seg_offset += imm * sizeof(void *);
397442
DEBUG_BIND("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED\n");
398443
break;
399444
case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
400445
count = uleb128(&ptr);
401446
skip = uleb128(&ptr);
402447
for (i = 0; i < count; i++) {
403-
set_bind_addr(&idx, plthook, base, sym_name, seg_index, seg_offset, segments);
448+
set_bind_addr(data, &idx, sym_name, seg_index, seg_offset);
404449
seg_offset += skip;
405450
}
406451
DEBUG_BIND("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB\n");
@@ -410,12 +455,12 @@ static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint3
410455
return idx;
411456
}
412457

413-
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)
458+
static void set_bind_addr(data_t *data, unsigned int *idx, const char *sym_name, int seg_index, int seg_offset)
414459
{
415-
if (plthook != NULL) {
416-
uint32_t vmaddr = segments[seg_index]->vmaddr;
417-
plthook->entries[*idx].name = sym_name;
418-
plthook->entries[*idx].addr = (void**)(base + vmaddr + seg_offset);
460+
if (data->plthook != NULL) {
461+
size_t vmaddr = data->segments[seg_index]->vmaddr;
462+
data->plthook->entries[*idx].name = sym_name;
463+
data->plthook->entries[*idx].addr = (void**)(vmaddr + data->slide + seg_offset);
419464
}
420465
(*idx)++;
421466
}

0 commit comments

Comments
 (0)