1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
25 int dummy_dyld_symbol
= 1;
30 // The following API's are deprecated.
31 // In Mac OS X 10.4 we only do a minimal implementation of these API's
32 // to keep from breaking existing clients (Omni and MS Crash Reporters).
34 // This minmal implementation only allows inspection of one process and
35 // only reveals the current images in that process (no notification of
36 // later image changes). It assumes both processes use the same dyld
37 // and dyld has not slid in either process.
41 #include "mach-o/dyld_debug.h"
42 #include "mach-o/dyld_gdb.h"
44 // global state set up by _dyld_debug_subscribe_to_events() and accessed by _dyld_debug_module_name()
45 static const struct dyld_image_info
* sImages
= NULL
;
46 static uint32_t sImagesCount
= 0;
47 static task_port_t sImagesTaskPort
= 0;
50 // reads an address range out of another process
51 // returns a malloc'ed block that caller should free
52 static void* xprocess_read(task_port_t target_task
, const void* address
, size_t len
)
55 mach_vm_address_t page_address
= (uint32_t)address
& (-4096);
56 mach_vm_address_t last_page_address
= ((uint32_t)address
+ len
+ 4095) & (-4096);
57 mach_vm_size_t page_size
= last_page_address
- page_address
;
60 kern_return_t r
= vm_read(
64 (vm_offset_t
*)&local_start
,
66 if ( r
== KERN_SUCCESS
) {
69 memcpy(result
, &local_start
[(uint32_t)address
- page_address
], len
);
70 vm_deallocate(mach_task_self(), (uintptr_t)local_start
, local_len
);
75 // reads a c-string out of another process. The returned string should be vm_deallocated.
76 // All strings must be less than 1 to 2 pages long
77 static const char* xprocess_read_string(task_port_t target_task
, const void* address
)
80 mach_vm_address_t page_address
= (uint32_t)address
& (-4096);
81 mach_vm_size_t page_size
= 0x2000; // always read two pages
84 kern_return_t r
= vm_read(
88 (vm_offset_t
*)&local_start
,
90 if ( r
== KERN_SUCCESS
) {
91 const char* str
= (char*)&local_start
[(uint32_t)address
- page_address
];
98 // SPI into dyld to get address of _dyld_get_all_image_infos data structure
99 static const struct dyld_all_image_infos
* dyld_get_all_image_infos()
101 static const struct dyld_all_image_infos
* (*p
)() = NULL
;
104 _dyld_func_lookup("__dyld_get_all_image_infos", (void**)&p
);
115 * _dyld_debug_module_name() is passed a dyld_debug_module struct and
116 * sets image_name and module_name as well as the nameCnts. If the module
117 * does not refer to a valid module DYLD_INVALID_ARGUMENTS is returned.
119 enum dyld_debug_return
120 _dyld_debug_module_name(
121 task_port_t target_task
,
122 unsigned long send_timeout
,
123 unsigned long rcv_timeout
,
124 boolean_t inconsistent_data_ok
,
125 struct dyld_debug_module
module,
127 unsigned long *image_nameCnt
,
129 unsigned long *module_nameCnt
)
131 // examine sImage* info set up by _dyld_debug_subscribe_to_events()
132 if ( sImagesTaskPort
== target_task
) {
134 for (i
=0; i
< sImagesCount
; ++i
) {
135 if ( module.header
== sImages
[i
].imageLoadAddress
) {
136 // copy requested string
137 const char* path
= xprocess_read_string(sImagesTaskPort
, sImages
[i
].imageFilePath
);
138 if ( path
!= NULL
) {
139 *image_name
= (char*)path
;
140 *image_nameCnt
= strlen(path
);
150 return DYLD_INVALID_ARGUMENTS
;
155 * set_dyld_debug_error_func() is called to register a function to be called
156 * when error occurs in the dyld debug API's.
159 _dyld_debug_set_error_func(
160 void (*func
)(struct dyld_debug_error_data
*e
))
167 // Examine a mach_header in another process and determine its slid
168 static ptrdiff_t slideForHeader(task_port_t target_task
, const struct mach_header
* otherAddressHeader
)
170 const struct mach_header
* mh
= xprocess_read(target_task
, otherAddressHeader
, 0x2000);
173 const struct segment_command
*sgp
=
174 (const struct segment_command
*)((char*)mh
+ sizeof(mh
));
176 for (i
= 0; i
< mh
->ncmds
; i
++){
177 if (sgp
->cmd
== LC_SEGMENT
) {
178 if (sgp
->fileoff
== 0 && sgp
->filesize
!= 0) {
179 return (uintptr_t)mh
- (uintptr_t)sgp
->vmaddr
;
182 sgp
= (const struct segment_command
*)((char *)sgp
+ sgp
->cmdsize
);
191 * _dyld_debug_subscribe_to_events creates a new thread that is will call the
192 * specified dyld_event_routine when dynamic link events occur in the target
193 * task. This uses _dyld_debug_add_event_subscriber() and is just a different
194 * interface to get events.
196 enum dyld_debug_return
197 _dyld_debug_subscribe_to_events(
198 task_port_t target_task
,
199 unsigned long send_timeout
,
200 unsigned long rcv_timeout
,
201 boolean_t inconsistent_data_ok
,
202 void (*dyld_event_routine
)(struct dyld_event event
))
207 // get location of dyld_get_all_image_infos in this process
208 // It is possible that dyld slid in one of the processes, in which case this fails
209 const struct dyld_all_image_infos
* infoOtherAddressSpace
= dyld_get_all_image_infos();
210 if ( infoOtherAddressSpace
!= NULL
) {
211 const struct dyld_all_image_infos
* infos
;
212 infos
= (const struct dyld_all_image_infos
*)xprocess_read(target_task
, infoOtherAddressSpace
, sizeof(struct dyld_all_image_infos
));
213 if ( infos
!= NULL
) {
214 // sanity check version
215 if ( infos
->version
== 1 ) {
216 sImages
= xprocess_read(target_task
, infos
->infoArray
, infos
->infoArrayCount
* sizeof(struct dyld_image_info
));
217 if ( sImages
!= NULL
) {
219 // save info info into sImage* globals for use by later calls to _dyld_debug_module_name()
220 sImagesCount
= infos
->infoArrayCount
;
221 sImagesTaskPort
= target_task
;
222 // tell caller about every image
223 for (i
=0; i
< infos
->infoArrayCount
; ++i
) {
224 struct dyld_event addEvent
;
225 bzero(&addEvent
, sizeof(struct dyld_event
));
226 const struct mach_header
* mh
= sImages
[i
].imageLoadAddress
;
227 addEvent
.type
= DYLD_IMAGE_ADDED
;
228 addEvent
.arg
[0].header
= (struct mach_header
*)mh
;
229 addEvent
.arg
[0].vmaddr_slide
= slideForHeader(target_task
, mh
);
230 addEvent
.arg
[0].module_index
= 0;
231 (*dyld_event_routine
)(addEvent
);
235 // we free the dyld_all_image_infos struct, but not the array we copied
236 // The array is left in sImage* for use by later calls to _dyld_debug_module_name()
241 // tell client event handler no more images
242 struct dyld_event event
;
243 event
.type
= DYLD_PAST_EVENTS_END
;
244 (*dyld_event_routine
)(event
);