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
);
132 static const struct libent
*
133 libent_lookup_bypathname_withhash(const char *nm
, const unsigned long hash
)
136 STAILQ_FOREACH(ll
, &libhead
, ll_linkage
) {
137 if (hash
!= ll
->ll_namehash
)
139 struct libent
*le
= &ll
->ll_entry
;
140 if (strcmp(nm
, le
->le_pathname
) == 0)
146 const struct libent
*
147 libent_lookup_byuuid(const uuid_t uuid
)
150 STAILQ_FOREACH(ll
, &libhead
, ll_linkage
) {
151 struct libent
*le
= &ll
->ll_entry
;
152 if (uuid_compare(uuid
, le
->le_uuid
) == 0)
158 const struct libent
*
159 libent_lookup_first_bytype(uint32_t mhtype
)
162 STAILQ_FOREACH(ll
, &libhead
, ll_linkage
) {
163 struct libent
*le
= &ll
->ll_entry
;
164 if (mhtype
== le
->le_mh
->filetype
)
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
)
173 const struct libent
*le
= libent_lookup_byuuid(uuid
);
175 return le
; // disallow multiple names for the same uuid
177 char *nm
= realpath(rawnm
, NULL
);
180 const unsigned long nmhash
= simple_namehash(nm
);
181 le
= libent_lookup_bypathname_withhash(nm
, nmhash
);
187 if (OPTIONS_DEBUG(opt
, 3)) {
189 uuid_unparse_lower(uuid
, uustr
);
190 printf("[adding <'%s', %s, 0x%llx, %p", nm
, uustr
, mhaddr
, mh
);
192 printf(" (%llx-%llx)", V_ADDR(vr
), V_ENDADDR(vr
));
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
;
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
;
207 ll
->ll_entry
.le_vr
= *vr
;
209 V_SETADDR(&ll
->ll_entry
.le_vr
, MACH_VM_MAX_ADDRESS
);
210 V_SETSIZE(&ll
->ll_entry
.le_vr
, 0);
212 ll
->ll_entry
.le_objoff
= objoff
;
213 STAILQ_INSERT_HEAD(&libhead
, ll
, ll_linkage
);
215 return &ll
->ll_entry
;
219 libent_build_nametable(task_t task
, dyld_process_info dpi
)
221 __block
bool valid
= true;
223 _dyld_process_info_for_each_image(dpi
, ^(uint64_t mhaddr
, const uuid_t uuid
, const char *path
) {
225 native_mach_header_t
*mh
= copy_dyld_image_mh(task
, mhaddr
, path
);
228 * Validate the rest of the mach information in the header before attempting optimizations
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
,
236 mach_vm_offset_t objoff
= MACH_VM_MAX_ADDRESS
;
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__
);
246 case NATIVE_LC_SEGMENT
: {
247 const native_segment_command_t
*sc
= (const void *)lc
;
250 strlcpy(scsegname
, sc
->segname
, sizeof (scsegname
));
252 if (0 == sc
->vmaddr
&&
253 strcmp(scsegname
, SEG_PAGEZERO
) == 0)
257 * -Depends- on finding a __TEXT segment first
258 * which implicitly maps the mach header too
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
);
267 printf("%s: expected %s segment, found %s\n", path
, SEG_TEXT
, scsegname
);
273 mach_vm_offset_t lo
= sc
->vmaddr
+ objoff
;
274 mach_vm_offset_t hi
= lo
+ sc
->vmsize
;
277 if (lo
< V_ADDR(&vr
)) {
278 mach_vm_offset_t newsize
= V_SIZE(&vr
) + (V_ADDR(&vr
) - lo
);
279 V_SETSIZE(&vr
, newsize
);
282 if (hi
> V_ENDADDR(&vr
)) {
283 V_SETSIZE(&vr
, (hi
- V_ADDR(&vr
)));
287 V_SETSIZE(&vr
, hi
- lo
);
289 assert(lo
>= V_ADDR(&vr
) && hi
<= V_ENDADDR(&vr
));
291 #if defined(RDAR_28040018)
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
;
303 if (NULL
== (lc
= next_lc(lc
)))
307 (void) libent_insert(path
, uuid
, mhaddr
, mh
, &vr
, objoff
);
311 if (OPTIONS_DEBUG(opt
, 3))
312 printf("nametable %sconstructed\n", valid
? "" : "NOT ");