]> git.saurik.com Git - apple/system_cmds.git/blame - gcore.tproj/dyld.c
system_cmds-735.tar.gz
[apple/system_cmds.git] / gcore.tproj / dyld.c
CommitLineData
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
37static const char warn_dyld_info[] = "dyld information is incomplete or damaged";
38
39dyld_process_info
40get_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 */
66bool
67get_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
79void
80free_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 */
89static native_mach_header_t *
90copy_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 */
125struct liblist {
126 STAILQ_ENTRY(liblist) ll_linkage;
127 unsigned long ll_namehash;
128 struct libent ll_entry;
129};
130static STAILQ_HEAD(, liblist) libhead = STAILQ_HEAD_INITIALIZER(libhead);
131
132static unsigned long
133namehash(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
142static const struct libent *
143libent_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
156const struct libent *
157libent_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
168const struct libent *
169libent_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
180const struct libent *
181libent_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
214bool
215libent_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}