Skip to content

Commit 80d18e7

Browse files
committed
refactored symbol resolving
1 parent 1968051 commit 80d18e7

10 files changed

Lines changed: 256 additions & 260 deletions

File tree

.clang-format

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ IndentWidth: 4
44
ColumnLimit: 120
55
AlignConsecutiveMacros: AcrossEmptyLines
66
AllowShortFunctionsOnASingleLine: false
7-
AllowShortIfStatementsOnASingleLine: true
7+
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
88
BreakAfterAttributes: Always
99
BreakBeforeBraces: Custom
1010
BraceWrapping:

include/tinyhook.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414
extern "C" {
1515
#endif
1616

17+
typedef enum {
18+
RESOLVE_ALL = 0,
19+
RESOLVE_EXPORT = 1 << 0,
20+
RESOLVE_SYMTAB = 1 << 1,
21+
RESOLVE_STUBS = 1 << 2
22+
} resolve_type_t;
23+
1724
typedef struct {
1825
void *address;
1926
int jump_size;
@@ -47,11 +54,7 @@ TH_VIS int read_mem(void *destination, const void *source, size_t len);
4754
TH_VIS int write_mem(void *destination, const void *source, size_t len);
4855

4956
/* symbol resolve */
50-
TH_VIS void *symtbl_solve(uint32_t image_index, const char *symbol_name);
51-
52-
TH_VIS void *symexp_solve(uint32_t image_index, const char *symbol_name);
53-
54-
TH_VIS void *symstub_solve(uint32_t image_index, const char *symbol_name);
57+
TH_VIS void *symbol_resolve(uint32_t image_index, const char *symbol_name, resolve_type_t type);
5558

5659
#ifdef __cplusplus
5760
}

src/interpose.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <mach-o/dyld.h>
55
#include <mach-o/loader.h>
66
#include <mach-o/nlist.h>
7+
#include <stdbool.h>
78
#include <stdint.h>
89
#include <string.h>
910
#ifndef COMPACT

src/private.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define TINYHOOK_PRIVATE_H
33

44
#include <TargetConditionals.h>
5+
#include <stdlib.h>
56

67
#include <mach/mach_init.h> // mach_task_self()
78
#if TARGET_OS_OSX

src/symbol.c

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
#include "private.h"
2+
#include "tinyhook.h"
3+
4+
#include <mach-o/dyld.h> // _dyld_*
5+
#include <mach-o/loader.h> // mach_header_64, load_command...
6+
#include <mach-o/nlist.h> // nlist_64
7+
#include <stdint.h>
8+
#include <string.h> // strcmp()
9+
10+
struct imageinfo {
11+
void *image_slide;
12+
void *image_header;
13+
void *linkedit_base;
14+
15+
uint32_t export_off;
16+
uint32_t string_off;
17+
uint32_t symbol_off;
18+
uint32_t indirectsym_off;
19+
20+
uint32_t ilocalsym; /* index to local symbols */
21+
uint32_t nlocalsym; /* number of local symbols */
22+
uint32_t iextdefsym; /* index to externally defined symbols */
23+
uint32_t nextdefsym; /* number of externally defined symbols */
24+
25+
uint64_t asymstub; /* addr of the stub section */
26+
uint32_t isymstub; /* starting index in indirect symbol table */
27+
uint32_t nsymstub; /* number of indirect symbol entries */
28+
uint32_t wsymstub; /* byte size of the stubs */
29+
};
30+
31+
static int parse_macho(uint32_t image_index, struct imageinfo *info) {
32+
const struct mach_header_64 *header = (struct mach_header_64 *)_dyld_get_image_header(image_index);
33+
if (header == NULL) {
34+
LOG_ERROR("parse_macho: image_index %d out of range!", image_index);
35+
return -1;
36+
}
37+
info->image_slide = (void *)_dyld_get_image_vmaddr_slide(image_index);
38+
info->image_header = (void *)header;
39+
40+
const struct load_command *cmd = (void *)header + sizeof(struct mach_header_64);
41+
for (int i = 0; i < header->ncmds; i++, cmd = (void *)cmd + cmd->cmdsize) {
42+
switch (cmd->cmd) {
43+
case LC_SEGMENT_64: {
44+
const struct segment_command_64 *seg = (struct segment_command_64 *)cmd;
45+
if (strcmp(seg->segname, SEG_LINKEDIT) == 0) {
46+
info->linkedit_base = info->image_slide + seg->vmaddr - seg->fileoff;
47+
break;
48+
}
49+
const struct section_64 *sects = (void *)seg + sizeof(const struct segment_command_64);
50+
if (strcmp(seg->segname, SEG_TEXT) == 0) {
51+
for (int j = 0; j < seg->nsects; j++) {
52+
if ((sects[j].flags & SECTION_TYPE) == S_SYMBOL_STUBS) {
53+
info->asymstub = sects[j].addr;
54+
info->isymstub = sects[j].reserved1;
55+
info->wsymstub = sects[j].reserved2;
56+
info->nsymstub = sects[j].size / info->wsymstub;
57+
break;
58+
}
59+
}
60+
}
61+
break;
62+
}
63+
case LC_DYLD_INFO:
64+
case LC_DYLD_INFO_ONLY: {
65+
const struct dyld_info_command *dyld_info = (struct dyld_info_command *)cmd;
66+
info->export_off = dyld_info->export_off;
67+
break;
68+
}
69+
case LC_DYLD_EXPORTS_TRIE: {
70+
const struct linkedit_data_command *linkedit_data = (struct linkedit_data_command *)cmd;
71+
info->export_off = linkedit_data->dataoff;
72+
break;
73+
}
74+
case LC_SYMTAB: {
75+
const struct symtab_command *symtab = (struct symtab_command *)cmd;
76+
info->string_off = symtab->stroff;
77+
info->symbol_off = symtab->symoff;
78+
break;
79+
}
80+
case LC_DYSYMTAB: {
81+
const struct dysymtab_command *dysymtab = (struct dysymtab_command *)cmd;
82+
info->ilocalsym = dysymtab->ilocalsym;
83+
info->nlocalsym = dysymtab->nlocalsym;
84+
info->iextdefsym = dysymtab->iextdefsym;
85+
info->nextdefsym = dysymtab->nextdefsym;
86+
info->indirectsym_off = dysymtab->indirectsymoff;
87+
break;
88+
}
89+
default:
90+
break;
91+
}
92+
}
93+
return 0;
94+
}
95+
96+
static void *resolve_symtab(struct imageinfo *info, const char *symbol_name) {
97+
uint64_t value = 0;
98+
const char *strtab = info->linkedit_base + info->string_off;
99+
const struct nlist_64 *symtab = info->linkedit_base + info->symbol_off;
100+
const struct mach_header_64 *header = info->image_header;
101+
102+
if (header->filetype != MH_DYLIB) {
103+
unsigned int l = info->iextdefsym, r = l + info->nextdefsym;
104+
while (l + 1 < r) {
105+
unsigned int mid = (l + r) / 2;
106+
if (strcmp(symbol_name, strtab + symtab[mid].n_un.n_strx) < 0) r = mid;
107+
else l = mid;
108+
}
109+
if (info->nextdefsym && strcmp(symbol_name, strtab + symtab[l].n_un.n_strx) == 0) {
110+
value = symtab[l].n_value;
111+
}
112+
}
113+
else {
114+
const struct nlist_64 *extdeftab = symtab + info->iextdefsym;
115+
for (int i = 0; i < info->nextdefsym; i++) {
116+
if (strcmp(symbol_name, strtab + extdeftab[i].n_un.n_strx) == 0) {
117+
value = extdeftab[i].n_value;
118+
break;
119+
}
120+
}
121+
}
122+
if (value != 0) return info->image_slide + value;
123+
124+
const struct nlist_64 *localtab = symtab + info->ilocalsym;
125+
for (int i = 0; i < info->nlocalsym; i++) {
126+
if (strcmp(symbol_name, strtab + localtab[i].n_un.n_strx) == 0) {
127+
value = localtab[i].n_value;
128+
break;
129+
}
130+
}
131+
if (value == 0) return NULL;
132+
return info->image_slide + value;
133+
}
134+
135+
static void *resolve_stub(struct imageinfo *info, const char *symbol_name) {
136+
uint64_t value = 0;
137+
const char *strtab = info->linkedit_base + info->string_off;
138+
const struct nlist_64 *symtab = info->linkedit_base + info->symbol_off;
139+
const uint32_t *indirsymtab = info->linkedit_base + info->indirectsym_off;
140+
141+
indirsymtab += info->isymstub;
142+
for (int i = 0; i < info->nsymstub; i++) {
143+
uint32_t idx = indirsymtab[i];
144+
if (idx & INDIRECT_SYMBOL_LOCAL) continue;
145+
if (strcmp(symbol_name, strtab + symtab[idx].n_un.n_strx) == 0) {
146+
value = info->asymstub + (uint64_t)i * info->wsymstub;
147+
break;
148+
}
149+
}
150+
151+
if (value == 0) return NULL;
152+
return info->image_slide + value;
153+
}
154+
155+
static uint64_t read_uleb128(const uint8_t **p) {
156+
int bit = 0;
157+
uint64_t result = 0;
158+
do {
159+
uint64_t slice = **p & 0x7f;
160+
result |= (slice << bit);
161+
bit += 7;
162+
} while (*(*p)++ & 0x80);
163+
return result;
164+
}
165+
166+
static uint64_t trie_query(const uint8_t *export, const char *name) {
167+
// documents in <mach-o/loader.h>
168+
uint64_t value = 0;
169+
uint64_t node_off = 0;
170+
const char *rest_name = name;
171+
bool go_child = true;
172+
while (go_child) {
173+
const uint8_t *cur_pos = export + node_off;
174+
uint64_t info_len = read_uleb128(&cur_pos);
175+
const uint8_t *child_off = cur_pos + info_len;
176+
if (rest_name[0] == '\0') {
177+
if (info_len != 0) {
178+
uint64_t flag = read_uleb128(&cur_pos);
179+
if (flag == EXPORT_SYMBOL_FLAGS_KIND_REGULAR) {
180+
value = read_uleb128(&cur_pos);
181+
}
182+
}
183+
break;
184+
}
185+
go_child = false;
186+
cur_pos = child_off;
187+
uint8_t child_count = *(uint8_t *)cur_pos++;
188+
for (int i = 0; i < child_count; i++) {
189+
char *cur_str = (char *)cur_pos;
190+
size_t cur_len = strlen(cur_str);
191+
cur_pos += cur_len + 1;
192+
uint64_t next_off = read_uleb128(&cur_pos);
193+
if (strncmp(rest_name, cur_str, cur_len) == 0) {
194+
go_child = true;
195+
rest_name += cur_len;
196+
node_off = next_off;
197+
break;
198+
}
199+
}
200+
}
201+
return value;
202+
}
203+
204+
static void *resolve_export(struct imageinfo *info, const char *symbol_name) {
205+
uint64_t value = 0;
206+
void *export_trie = info->linkedit_base + info->export_off;
207+
208+
value = trie_query(export_trie, symbol_name);
209+
210+
if (value == 0) return NULL;
211+
return info->image_header + value;
212+
}
213+
214+
void *symbol_resolve(uint32_t image_index, const char *symbol_name, resolve_type_t type) {
215+
ARG_CHECK(symbol_name != NULL);
216+
struct imageinfo info;
217+
memset(&info, 0, sizeof(struct imageinfo));
218+
219+
if (parse_macho(image_index, &info) != 0) return NULL;
220+
if (!info.linkedit_base) {
221+
LOG_ERROR("symbol_resolve: __LINKEDIT segment not found in image with index %d!", image_index);
222+
return NULL;
223+
}
224+
225+
void *symbol_address = NULL;
226+
resolve_type_t resolve_type = type;
227+
if (resolve_type == RESOLVE_ALL) resolve_type = RESOLVE_EXPORT | RESOLVE_SYMTAB | RESOLVE_STUBS;
228+
if ((resolve_type & RESOLVE_EXPORT) && info.export_off) {
229+
symbol_address = resolve_export(&info, symbol_name);
230+
if (symbol_address) return symbol_address;
231+
}
232+
if ((resolve_type & RESOLVE_SYMTAB) && info.symbol_off && info.nextdefsym + info.nlocalsym) {
233+
symbol_address = resolve_symtab(&info, symbol_name);
234+
if (symbol_address) return symbol_address;
235+
}
236+
if ((resolve_type & RESOLVE_STUBS) && info.indirectsym_off && info.symbol_off && info.asymstub) {
237+
symbol_address = resolve_stub(&info, symbol_name);
238+
if (symbol_address) return symbol_address;
239+
}
240+
241+
return symbol_address;
242+
}

0 commit comments

Comments
 (0)