]> git.cameronkatri.com Git - apple_cmds.git/blob - system_cmds/gcore.tproj/dyld.c
Merge branch 'apple'
[apple_cmds.git] / system_cmds / gcore.tproj / dyld.c
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 const struct libent *
133 libent_lookup_bypathname_withhash(const char *nm, const unsigned long hash)
134 {
135 struct liblist *ll;
136 STAILQ_FOREACH(ll, &libhead, ll_linkage) {
137 if (hash != ll->ll_namehash)
138 continue;
139 struct libent *le = &ll->ll_entry;
140 if (strcmp(nm, le->le_pathname) == 0)
141 return le;
142 }
143 return NULL;
144 }
145
146 const struct libent *
147 libent_lookup_byuuid(const uuid_t uuid)
148 {
149 struct liblist *ll;
150 STAILQ_FOREACH(ll, &libhead, ll_linkage) {
151 struct libent *le = &ll->ll_entry;
152 if (uuid_compare(uuid, le->le_uuid) == 0)
153 return le;
154 }
155 return NULL;
156 }
157
158 const struct libent *
159 libent_lookup_first_bytype(uint32_t mhtype)
160 {
161 struct liblist *ll;
162 STAILQ_FOREACH(ll, &libhead, ll_linkage) {
163 struct libent *le = &ll->ll_entry;
164 if (mhtype == le->le_mh->filetype)
165 return le;
166 }
167 return NULL;
168 }
169
170 const struct libent *
171 libent_insert(const char *rawnm, const uuid_t uuid, uint64_t mhaddr, const native_mach_header_t *mh, const struct vm_range *vr, mach_vm_offset_t objoff)
172 {
173 const struct libent *le = libent_lookup_byuuid(uuid);
174 if (NULL != le)
175 return le; // disallow multiple names for the same uuid
176
177 char *nm = realpath(rawnm, NULL);
178 if (NULL == nm)
179 nm = strdup(rawnm);
180 const unsigned long nmhash = simple_namehash(nm);
181 le = libent_lookup_bypathname_withhash(nm, nmhash);
182 if (NULL != le) {
183 free(nm);
184 return le;
185 }
186
187 if (OPTIONS_DEBUG(opt, 3)) {
188 uuid_string_t uustr;
189 uuid_unparse_lower(uuid, uustr);
190 printf("[adding <'%s', %s, 0x%llx, %p", nm, uustr, mhaddr, mh);
191 if (vr)
192 printf(" (%llx-%llx)", V_ADDR(vr), V_ENDADDR(vr));
193 printf(">]\n");
194 }
195 struct liblist *ll = malloc(sizeof (*ll));
196 ll->ll_namehash = nmhash;
197 ll->ll_entry.le_pathname = nm;
198 ll->ll_entry.le_filename = strrchr(ll->ll_entry.le_pathname, '/');
199 if (NULL == ll->ll_entry.le_filename)
200 ll->ll_entry.le_filename = ll->ll_entry.le_pathname;
201 else
202 ll->ll_entry.le_filename++;
203 uuid_copy(ll->ll_entry.le_uuid, uuid);
204 ll->ll_entry.le_mhaddr = mhaddr;
205 ll->ll_entry.le_mh = mh;
206 if (vr)
207 ll->ll_entry.le_vr = *vr;
208 else {
209 V_SETADDR(&ll->ll_entry.le_vr, MACH_VM_MAX_ADDRESS);
210 V_SETSIZE(&ll->ll_entry.le_vr, 0);
211 }
212 ll->ll_entry.le_objoff = objoff;
213 STAILQ_INSERT_HEAD(&libhead, ll, ll_linkage);
214
215 return &ll->ll_entry;
216 }
217
218 bool
219 libent_build_nametable(task_t task, dyld_process_info dpi)
220 {
221 __block bool valid = true;
222
223 _dyld_process_info_for_each_image(dpi, ^(uint64_t mhaddr, const uuid_t uuid, const char *path) {
224 if (valid) {
225 native_mach_header_t *mh = copy_dyld_image_mh(task, mhaddr, path);
226 if (mh) {
227 /*
228 * Validate the rest of the mach information in the header before attempting optimizations
229 */
230 const size_t mhlen = sizeof (*mh) + mh->sizeofcmds;
231 const struct load_command *lc = (const void *)(mh + 1);
232 struct vm_range vr = {
233 .addr = MACH_VM_MAX_ADDRESS,
234 .size = 0
235 };
236 mach_vm_offset_t objoff = MACH_VM_MAX_ADDRESS;
237
238 for (unsigned n = 0; n < mh->ncmds; n++) {
239 if (((uintptr_t)lc & 0x3) != 0 ||
240 (uintptr_t)lc < (uintptr_t)mh || (uintptr_t)lc > (uintptr_t)mh + mhlen) {
241 warnx("%s, %d", warn_dyld_info, __LINE__);
242 valid = false;
243 break;
244 }
245 switch (lc->cmd) {
246 case NATIVE_LC_SEGMENT: {
247 const native_segment_command_t *sc = (const void *)lc;
248
249 char scsegname[17];
250 strlcpy(scsegname, sc->segname, sizeof (scsegname));
251
252 if (0 == sc->vmaddr &&
253 strcmp(scsegname, SEG_PAGEZERO) == 0)
254 break;
255
256 /*
257 * -Depends- on finding a __TEXT segment first
258 * which implicitly maps the mach header too
259 */
260
261 if (MACH_VM_MAX_ADDRESS == objoff) {
262 if (strcmp(scsegname, SEG_TEXT) == 0) {
263 objoff = mhaddr - sc->vmaddr;
264 V_SETADDR(&vr, mhaddr);
265 V_SETSIZE(&vr, sc->vmsize);
266 } else {
267 printf("%s: expected %s segment, found %s\n", path, SEG_TEXT, scsegname);
268 valid = false;
269 break;
270 }
271 }
272
273 mach_vm_offset_t lo = sc->vmaddr + objoff;
274 mach_vm_offset_t hi = lo + sc->vmsize;
275
276 if (V_SIZE(&vr)) {
277 if (lo < V_ADDR(&vr)) {
278 mach_vm_offset_t newsize = V_SIZE(&vr) + (V_ADDR(&vr) - lo);
279 V_SETSIZE(&vr, newsize);
280 V_SETADDR(&vr, lo);
281 }
282 if (hi > V_ENDADDR(&vr)) {
283 V_SETSIZE(&vr, (hi - V_ADDR(&vr)));
284 }
285 } else {
286 V_SETADDR(&vr, lo);
287 V_SETSIZE(&vr, hi - lo);
288 }
289 assert(lo >= V_ADDR(&vr) && hi <= V_ENDADDR(&vr));
290 } break;
291 #if defined(RDAR_28040018)
292 case LC_ID_DYLINKER:
293 if (MH_DYLINKER == mh->filetype) {
294 /* workaround: the API doesn't always return the right name */
295 const struct dylinker_command *dc = (const void *)lc;
296 path = dc->name.offset + (const char *)dc;
297 }
298 break;
299 #endif
300 default:
301 break;
302 }
303 if (NULL == (lc = next_lc(lc)))
304 break;
305 }
306 if (valid)
307 (void) libent_insert(path, uuid, mhaddr, mh, &vr, objoff);
308 }
309 }
310 });
311 if (OPTIONS_DEBUG(opt, 3))
312 printf("nametable %sconstructed\n", valid ? "" : "NOT ");
313 return valid;
314 }