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;
29 // The following API's are deprecated.
30 // In Mac OS X 10.4 we only do a minimal implementation of these API's
31 // to keep from breaking existing clients (Omni and MS Crash Reporters).
33 // This minmal implementation only allows inspection of one process and
34 // only reveals the current images in that process (no notification of
35 // later image changes). It assumes both processes use the same dyld
36 // and dyld has not slid in either process.
40 #include "mach-o/dyld_debug.h"
41 #include "mach-o/dyld_gdb.h"
43 // global state set up by _dyld_debug_subscribe_to_events() and accessed by _dyld_debug_module_name()
44 static const struct dyld_image_info
* sImages
= NULL
;
45 static uint32_t sImagesCount
= 0;
46 static task_port_t sImagesTaskPort
= 0;
49 // reads an address range out of another process
50 // returns a malloc'ed block that caller should free
51 static void* xprocess_read(task_port_t target_task
, const void* address
, size_t len
)
54 mach_vm_address_t page_address
= (uint32_t)address
& (-4096);
55 mach_vm_address_t last_page_address
= ((uint32_t)address
+ len
+ 4095) & (-4096);
56 mach_vm_size_t page_size
= last_page_address
- page_address
;
59 kern_return_t r
= vm_read(
63 (vm_offset_t
*)&local_start
,
65 if ( r
== KERN_SUCCESS
) {
68 memcpy(result
, &local_start
[(uint32_t)address
- page_address
], len
);
69 vm_deallocate(mach_task_self(), (uintptr_t)local_start
, local_len
);
74 // reads a c-string out of another process. The returned string should be vm_deallocated.
75 // All strings must be less than 1 to 2 pages long
76 static const char* xprocess_read_string(task_port_t target_task
, const void* address
)
79 mach_vm_address_t page_address
= (uint32_t)address
& (-4096);
80 mach_vm_size_t page_size
= 0x2000; // always read two pages
83 kern_return_t r
= vm_read(
87 (vm_offset_t
*)&local_start
,
89 if ( r
== KERN_SUCCESS
) {
90 const char* str
= (char*)&local_start
[(uint32_t)address
- page_address
];
97 // SPI into dyld to get address of _dyld_get_all_image_infos data structure
98 static const struct dyld_all_image_infos
* dyld_get_all_image_infos()
100 static const struct dyld_all_image_infos
* (*p
)() = NULL
;
103 _dyld_func_lookup("__dyld_get_all_image_infos", (void**)&p
);
114 * _dyld_debug_module_name() is passed a dyld_debug_module struct and
115 * sets image_name and module_name as well as the nameCnts. If the module
116 * does not refer to a valid module DYLD_INVALID_ARGUMENTS is returned.
118 enum dyld_debug_return
119 _dyld_debug_module_name(
120 task_port_t target_task
,
121 unsigned long send_timeout
,
122 unsigned long rcv_timeout
,
123 boolean_t inconsistent_data_ok
,
124 struct dyld_debug_module
module,
126 unsigned long *image_nameCnt
,
128 unsigned long *module_nameCnt
)
130 // examine sImage* info set up by _dyld_debug_subscribe_to_events()
131 if ( sImagesTaskPort
== target_task
) {
133 for (i
=0; i
< sImagesCount
; ++i
) {
134 if ( module.header
== sImages
[i
].imageLoadAddress
) {
135 // copy requested string
136 const char* path
= xprocess_read_string(sImagesTaskPort
, sImages
[i
].imageFilePath
);
137 if ( path
!= NULL
) {
138 *image_name
= (char*)path
;
139 *image_nameCnt
= strlen(path
);
149 return DYLD_INVALID_ARGUMENTS
;
154 * set_dyld_debug_error_func() is called to register a function to be called
155 * when error occurs in the dyld debug API's.
158 _dyld_debug_set_error_func(
159 void (*func
)(struct dyld_debug_error_data
*e
))
166 // Examine a mach_header in another process and determine its slide
167 static ptrdiff_t slideForHeader(task_port_t target_task
, const struct mach_header
* otherAddressHeader
)
169 ptrdiff_t result
= 0;
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 result
= (uintptr_t)mh
- (uintptr_t)sgp
->vmaddr
;
183 sgp
= (const struct segment_command
*)((char *)sgp
+ sgp
->cmdsize
);
192 * _dyld_debug_subscribe_to_events creates a new thread that is will call the
193 * specified dyld_event_routine when dynamic link events occur in the target
194 * task. This uses _dyld_debug_add_event_subscriber() and is just a different
195 * interface to get events.
197 enum dyld_debug_return
198 _dyld_debug_subscribe_to_events(
199 task_port_t target_task
,
200 unsigned long send_timeout
,
201 unsigned long rcv_timeout
,
202 boolean_t inconsistent_data_ok
,
203 void (*dyld_event_routine
)(struct dyld_event event
))
208 // get location of dyld_get_all_image_infos in this process
209 // It is possible that dyld slid in one of the processes, in which case this fails
210 const struct dyld_all_image_infos
* infoOtherAddressSpace
= dyld_get_all_image_infos();
211 if ( infoOtherAddressSpace
!= NULL
) {
212 const struct dyld_all_image_infos
* infos
;
213 infos
= (const struct dyld_all_image_infos
*)xprocess_read(target_task
, infoOtherAddressSpace
, sizeof(struct dyld_all_image_infos
));
214 if ( infos
!= NULL
) {
215 // sanity check version
216 if ( infos
->version
== 1 ) {
217 sImages
= xprocess_read(target_task
, infos
->infoArray
, infos
->infoArrayCount
* sizeof(struct dyld_image_info
));
218 if ( sImages
!= NULL
) {
220 // save info info into sImage* globals for use by later calls to _dyld_debug_module_name()
221 sImagesCount
= infos
->infoArrayCount
;
222 sImagesTaskPort
= target_task
;
223 // tell caller about every image
224 for (i
=0; i
< infos
->infoArrayCount
; ++i
) {
225 struct dyld_event addEvent
;
226 bzero(&addEvent
, sizeof(struct dyld_event
));
227 const struct mach_header
* mh
= sImages
[i
].imageLoadAddress
;
228 addEvent
.type
= DYLD_IMAGE_ADDED
;
229 addEvent
.arg
[0].header
= (struct mach_header
*)mh
;
230 addEvent
.arg
[0].vmaddr_slide
= slideForHeader(target_task
, mh
);
231 addEvent
.arg
[0].module_index
= 0;
232 (*dyld_event_routine
)(addEvent
);
236 // we free the dyld_all_image_infos struct, but not the array we copied
237 // The array is left in sImage* for use by later calls to _dyld_debug_module_name()
242 // tell client event handler no more images
243 struct dyld_event event
;
244 event
.type
= DYLD_PAST_EVENTS_END
;
245 (*dyld_event_routine
)(event
);