]>
Commit | Line | Data |
---|---|---|
cf37c299 A |
1 | /* |
2 | * Copyright (c) 2016 Apple Inc. All rights reserved. | |
3 | */ | |
4 | ||
5 | #include "options.h" | |
6 | #include "dyld.h" | |
7 | #include "utils.h" | |
8 | #include "corefile.h" | |
9 | #include "vm.h" | |
10 | ||
11 | #include <mach-o/loader.h> | |
12 | #include <mach-o/fat.h> | |
13 | #include <mach-o/dyld_process_info.h> | |
14 | ||
15 | #include <mach/mach.h> | |
16 | #include <mach/task.h> | |
17 | #include <mach/mach_vm.h> | |
18 | #include <mach/shared_region.h> | |
19 | #include <sys/param.h> | |
20 | #include <stdio.h> | |
21 | #include <stdlib.h> | |
22 | #include <unistd.h> | |
23 | #include <time.h> | |
24 | #include <libgen.h> | |
25 | #include <sys/stat.h> | |
26 | ||
27 | /* | |
28 | * WARNING WARNING WARNING | |
29 | * | |
30 | * Do not trust any of the data from the target task. | |
31 | * | |
32 | * A broken program may have damaged it, or a malicious | |
33 | * program may have deliberately constructed something to | |
34 | * cause us harm. | |
35 | */ | |
36 | ||
37 | static const char warn_dyld_info[] = "dyld information is incomplete or damaged"; | |
38 | ||
39 | dyld_process_info | |
40 | get_task_dyld_info(const task_t task) | |
41 | { | |
42 | kern_return_t kret; | |
43 | dyld_process_info dpi = _dyld_process_info_create(task, 0, &kret); | |
44 | if (NULL == dpi) { | |
45 | err_mach(kret, NULL, "_dlyd_process_info_create"); | |
46 | } else { | |
47 | dyld_process_state_info stateInfo; | |
48 | ||
49 | _dyld_process_info_get_state(dpi, &stateInfo); | |
50 | switch (stateInfo.dyldState) { | |
51 | case dyld_process_state_not_started: | |
52 | warnx("%s: dyld state %d", warn_dyld_info, stateInfo.dyldState); | |
53 | _dyld_process_info_release(dpi); | |
54 | dpi = NULL; | |
55 | break; | |
56 | default: | |
57 | break; | |
58 | } | |
59 | } | |
60 | return dpi; | |
61 | } | |
62 | ||
63 | /* | |
64 | * Get the shared cache UUID iff it's in use and is the system one | |
65 | */ | |
66 | bool | |
67 | get_sc_uuid(dyld_process_info dpi, uuid_t uu) | |
68 | { | |
69 | dyld_process_cache_info cacheInfo; | |
70 | ||
71 | _dyld_process_info_get_cache(dpi, &cacheInfo); | |
72 | if (!cacheInfo.noCache && !cacheInfo.privateCache) { | |
73 | uuid_copy(uu, cacheInfo.cacheUUID); | |
74 | return true; | |
75 | } | |
76 | return false; | |
77 | } | |
78 | ||
79 | void | |
80 | free_task_dyld_info(dyld_process_info dpi) | |
81 | { | |
82 | _dyld_process_info_release(dpi); | |
83 | } | |
84 | ||
85 | /* | |
86 | * This routine collects both the Mach-O header and the commands | |
87 | * "below" it, assuming they're in contiguous memory. | |
88 | */ | |
89 | static native_mach_header_t * | |
90 | copy_dyld_image_mh(task_t task, mach_vm_address_t targetmh, const char *path) | |
91 | { | |
92 | vm_offset_t mhaddr = 0; | |
93 | mach_msg_type_number_t mhlen = sizeof (native_mach_header_t); | |
94 | ||
95 | for (int attempts = 0; attempts < 2; attempts++) { | |
96 | ||
97 | const kern_return_t ret = mach_vm_read(task, targetmh, mhlen, &mhaddr, &mhlen); | |
98 | if (KERN_SUCCESS != ret) { | |
99 | err_mach(ret, NULL, "mach_vm_read() at 0x%llx for image %s\n", targetmh, path); | |
100 | mhaddr = 0; | |
101 | break; | |
102 | } | |
103 | const native_mach_header_t *mh = (void *)mhaddr; | |
104 | if (mhlen < mh->sizeofcmds + sizeof (*mh)) { | |
105 | const mach_msg_type_number_t newmhlen = sizeof (*mh) + mh->sizeofcmds; | |
106 | mach_vm_deallocate(mach_task_self(), mhaddr, mhlen); | |
107 | mhlen = newmhlen; | |
108 | } else | |
109 | break; | |
110 | } | |
111 | ||
112 | native_mach_header_t *result = NULL; | |
113 | ||
114 | if (mhaddr) { | |
115 | if (NULL != (result = malloc(mhlen))) | |
116 | memcpy(result, (void *)mhaddr, mhlen); | |
117 | mach_vm_deallocate(mach_task_self(), mhaddr, mhlen); | |
118 | } | |
119 | return result; | |
120 | } | |
121 | ||
122 | /* | |
123 | * This table (list) describes libraries and the executable in the address space | |
124 | */ | |
125 | struct liblist { | |
126 | STAILQ_ENTRY(liblist) ll_linkage; | |
127 | unsigned long ll_namehash; | |
128 | struct libent ll_entry; | |
129 | }; | |
130 | static STAILQ_HEAD(, liblist) libhead = STAILQ_HEAD_INITIALIZER(libhead); | |
131 | ||
132 | static unsigned long | |
133 | namehash(const char *nm) | |
134 | { | |
135 | unsigned long result = 5381; | |
136 | int c; | |
137 | while (0 != (c = *nm++)) | |
138 | result = (result * 33) ^ c; | |
139 | return result; /* modified djb2 */ | |
140 | } | |
141 | ||
142 | static const struct libent * | |
143 | libent_lookup_bypathname_withhash(const char *nm, const unsigned long hash) | |
144 | { | |
145 | struct liblist *ll; | |
146 | STAILQ_FOREACH(ll, &libhead, ll_linkage) { | |
147 | if (hash != ll->ll_namehash) | |
148 | continue; | |
149 | struct libent *le = &ll->ll_entry; | |
150 | if (strcmp(nm, le->le_pathname) == 0) | |
151 | return le; | |
152 | } | |
153 | return NULL; | |
154 | } | |
155 | ||
156 | const struct libent * | |
157 | libent_lookup_byuuid(const uuid_t uuid) | |
158 | { | |
159 | struct liblist *ll; | |
160 | STAILQ_FOREACH(ll, &libhead, ll_linkage) { | |
161 | struct libent *le = &ll->ll_entry; | |
162 | if (uuid_compare(uuid, le->le_uuid) == 0) | |
163 | return le; | |
164 | } | |
165 | return NULL; | |
166 | } | |
167 | ||
168 | const struct libent * | |
169 | libent_lookup_first_bytype(uint32_t mhtype) | |
170 | { | |
171 | struct liblist *ll; | |
172 | STAILQ_FOREACH(ll, &libhead, ll_linkage) { | |
173 | struct libent *le = &ll->ll_entry; | |
174 | if (mhtype == le->le_mh->filetype) | |
175 | return le; | |
176 | } | |
177 | return NULL; | |
178 | } | |
179 | ||
180 | const struct libent * | |
181 | libent_insert(const char *nm, const uuid_t uuid, uint64_t mhaddr, const native_mach_header_t *mh) | |
182 | { | |
183 | const struct libent *le = libent_lookup_byuuid(uuid); | |
184 | if (NULL != le) | |
185 | return le; // disallow multiple names for the same uuid | |
186 | ||
187 | unsigned long nmhash = namehash(nm); | |
188 | le = libent_lookup_bypathname_withhash(nm, nmhash); | |
189 | if (NULL != le) | |
190 | return le; | |
191 | ||
192 | if (opt->debug > 3) { | |
193 | uuid_string_t uustr; | |
194 | uuid_unparse_lower(uuid, uustr); | |
195 | printf("[adding <'%s', %s, 0x%llx, %p>]\n", nm, uustr, mhaddr, mh); | |
196 | } | |
197 | struct liblist *ll = malloc(sizeof (*ll)); | |
198 | ll->ll_namehash = nmhash; | |
199 | ll->ll_entry.le_pathname = strdup(nm); | |
200 | ll->ll_entry.le_filename = strrchr(ll->ll_entry.le_pathname, '/'); | |
201 | if (NULL == ll->ll_entry.le_filename) | |
202 | ll->ll_entry.le_filename = ll->ll_entry.le_pathname; | |
203 | else | |
204 | ll->ll_entry.le_filename++; | |
205 | uuid_copy(ll->ll_entry.le_uuid, uuid); | |
206 | ll->ll_entry.le_mhaddr = mhaddr; | |
207 | ll->ll_entry.le_mh = mh; | |
208 | ||
209 | STAILQ_INSERT_HEAD(&libhead, ll, ll_linkage); | |
210 | ||
211 | return &ll->ll_entry; | |
212 | } | |
213 | ||
214 | bool | |
215 | libent_build_nametable(task_t task, dyld_process_info dpi) | |
216 | { | |
217 | __block bool valid = true; | |
218 | ||
219 | _dyld_process_info_for_each_image(dpi, ^(uint64_t mhaddr, const uuid_t uuid, const char *path) { | |
220 | if (valid) { | |
221 | native_mach_header_t *mh = copy_dyld_image_mh(task, mhaddr, path); | |
222 | if (mh) { | |
223 | /* | |
224 | * Validate the rest of the mach information in the header before attempting optimizations | |
225 | */ | |
226 | const size_t mhlen = sizeof (*mh) + mh->sizeofcmds; | |
227 | const struct load_command *lc = (const void *)(mh + 1); | |
228 | ||
229 | for (unsigned n = 0; n < mh->ncmds; n++) { | |
230 | if (((uintptr_t)lc & 0x3) != 0 || | |
231 | (uintptr_t)lc < (uintptr_t)mh || (uintptr_t)lc > (uintptr_t)mh + mhlen) { | |
232 | warnx("%s, %d", warn_dyld_info, __LINE__); | |
233 | valid = false; | |
234 | break; | |
235 | } | |
236 | if (lc->cmdsize) | |
237 | lc = (const void *)((caddr_t)lc + lc->cmdsize); | |
238 | else | |
239 | break; | |
240 | } | |
241 | if (valid) | |
242 | (void) libent_insert(path, uuid, mhaddr, mh); | |
243 | } | |
244 | } | |
245 | }); | |
246 | if (opt->debug) | |
247 | printf("nametable %sconstructed\n", valid ? "" : "NOT "); | |
248 | return valid; | |
249 | } |