2 * Copyright (c) 2016 Apple Inc. All rights reserved.
11 #include <mach-o/loader.h>
12 #include <mach-o/fat.h>
13 #include <mach-o/dyld_process_info.h>
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>
28 * WARNING WARNING WARNING
30 * Do not trust any of the data from the target task.
32 * A broken program may have damaged it, or a malicious
33 * program may have deliberately constructed something to
37 static const char warn_dyld_info
[] = "dyld information is incomplete or damaged";
40 get_task_dyld_info(const task_t task
)
43 dyld_process_info dpi
= _dyld_process_info_create(task
, 0, &kret
);
45 err_mach(kret
, NULL
, "_dlyd_process_info_create");
47 dyld_process_state_info stateInfo
;
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
);
64 * Get the shared cache UUID iff it's in use and is the system one
67 get_sc_uuid(dyld_process_info dpi
, uuid_t uu
)
69 dyld_process_cache_info cacheInfo
;
71 _dyld_process_info_get_cache(dpi
, &cacheInfo
);
72 if (!cacheInfo
.noCache
&& !cacheInfo
.privateCache
) {
73 uuid_copy(uu
, cacheInfo
.cacheUUID
);
80 free_task_dyld_info(dyld_process_info dpi
)
82 _dyld_process_info_release(dpi
);
86 * This routine collects both the Mach-O header and the commands
87 * "below" it, assuming they're in contiguous memory.
89 static native_mach_header_t
*
90 copy_dyld_image_mh(task_t task
, mach_vm_address_t targetmh
, const char *path
)
92 vm_offset_t mhaddr
= 0;
93 mach_msg_type_number_t mhlen
= sizeof (native_mach_header_t
);
95 for (int attempts
= 0; attempts
< 2; attempts
++) {
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
);
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
);
112 native_mach_header_t
*result
= NULL
;
115 if (NULL
!= (result
= malloc(mhlen
)))
116 memcpy(result
, (void *)mhaddr
, mhlen
);
117 mach_vm_deallocate(mach_task_self(), mhaddr
, mhlen
);
123 * This table (list) describes libraries and the executable in the address space
126 STAILQ_ENTRY(liblist
) ll_linkage
;
127 unsigned long ll_namehash
;
128 struct libent ll_entry
;
130 static STAILQ_HEAD(, liblist
) libhead
= STAILQ_HEAD_INITIALIZER(libhead
);
133 namehash(const char *nm
)
135 unsigned long result
= 5381;
137 while (0 != (c
= *nm
++))
138 result
= (result
* 33) ^ c
;
139 return result
; /* modified djb2 */
142 static const struct libent
*
143 libent_lookup_bypathname_withhash(const char *nm
, const unsigned long hash
)
146 STAILQ_FOREACH(ll
, &libhead
, ll_linkage
) {
147 if (hash
!= ll
->ll_namehash
)
149 struct libent
*le
= &ll
->ll_entry
;
150 if (strcmp(nm
, le
->le_pathname
) == 0)
156 const struct libent
*
157 libent_lookup_byuuid(const uuid_t uuid
)
160 STAILQ_FOREACH(ll
, &libhead
, ll_linkage
) {
161 struct libent
*le
= &ll
->ll_entry
;
162 if (uuid_compare(uuid
, le
->le_uuid
) == 0)
168 const struct libent
*
169 libent_lookup_first_bytype(uint32_t mhtype
)
172 STAILQ_FOREACH(ll
, &libhead
, ll_linkage
) {
173 struct libent
*le
= &ll
->ll_entry
;
174 if (mhtype
== le
->le_mh
->filetype
)
180 const struct libent
*
181 libent_insert(const char *nm
, const uuid_t uuid
, uint64_t mhaddr
, const native_mach_header_t
*mh
)
183 const struct libent
*le
= libent_lookup_byuuid(uuid
);
185 return le
; // disallow multiple names for the same uuid
187 unsigned long nmhash
= namehash(nm
);
188 le
= libent_lookup_bypathname_withhash(nm
, nmhash
);
192 if (opt
->debug
> 3) {
194 uuid_unparse_lower(uuid
, uustr
);
195 printf("[adding <'%s', %s, 0x%llx, %p>]\n", nm
, uustr
, mhaddr
, mh
);
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
;
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
;
209 STAILQ_INSERT_HEAD(&libhead
, ll
, ll_linkage
);
211 return &ll
->ll_entry
;
215 libent_build_nametable(task_t task
, dyld_process_info dpi
)
217 __block
bool valid
= true;
219 _dyld_process_info_for_each_image(dpi
, ^(uint64_t mhaddr
, const uuid_t uuid
, const char *path
) {
221 native_mach_header_t
*mh
= copy_dyld_image_mh(task
, mhaddr
, path
);
224 * Validate the rest of the mach information in the header before attempting optimizations
226 const size_t mhlen
= sizeof (*mh
) + mh
->sizeofcmds
;
227 const struct load_command
*lc
= (const void *)(mh
+ 1);
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__
);
237 lc
= (const void *)((caddr_t
)lc
+ lc
->cmdsize
);
242 (void) libent_insert(path
, uuid
, mhaddr
, mh
);
247 printf("nametable %sconstructed\n", valid
? "" : "NOT ");