dyld-46.16.tar.gz
[apple/dyld.git] / src / dyld_debug.c
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
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
12 * file.
13 *
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.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 int dummy_dyld_symbol = 1;
26
27 #include <stdio.h>
28 #include <stdlib.h>
29
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).
33 //
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.
38 //
39 #if __ppc__
40
41 #include "mach-o/dyld_debug.h"
42 #include "mach-o/dyld_gdb.h"
43
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;
48
49
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)
53 {
54 void* result = NULL;
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;
58 uint8_t* local_start;
59 uint32_t local_len;
60 kern_return_t r = vm_read(
61 target_task,
62 page_address,
63 page_size,
64 (vm_offset_t*)&local_start,
65 &local_len);
66 if ( r == KERN_SUCCESS ) {
67 result = malloc(len);
68 if ( result != NULL )
69 memcpy(result, &local_start[(uint32_t)address - page_address], len);
70 vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_len);
71 }
72 return result;
73 }
74
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)
78 {
79 char* result = NULL;
80 mach_vm_address_t page_address = (uint32_t)address & (-4096);
81 mach_vm_size_t page_size = 0x2000; // always read two pages
82 uint8_t* local_start;
83 uint32_t local_len;
84 kern_return_t r = vm_read(
85 target_task,
86 page_address,
87 page_size,
88 (vm_offset_t*)&local_start,
89 &local_len);
90 if ( r == KERN_SUCCESS ) {
91 const char* str = (char*)&local_start[(uint32_t)address - page_address];
92 return str;
93 }
94 return result;
95 }
96
97
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()
100 {
101 static const struct dyld_all_image_infos* (*p)() = NULL;
102
103 if ( p == NULL )
104 _dyld_func_lookup("__dyld_get_all_image_infos", (void**)&p);
105
106 if ( p != NULL )
107 return p();
108 else
109 return NULL;
110 }
111
112
113
114 /*
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.
118 */
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,
126 char **image_name,
127 unsigned long *image_nameCnt,
128 char **module_name,
129 unsigned long *module_nameCnt)
130 {
131 // examine sImage* info set up by _dyld_debug_subscribe_to_events()
132 if ( sImagesTaskPort == target_task ) {
133 unsigned int i;
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);
141 *module_name = NULL;
142 *module_nameCnt = 0;
143 return DYLD_SUCCESS;
144 }
145 }
146 }
147 }
148
149 // not supported
150 return DYLD_INVALID_ARGUMENTS;
151 }
152
153
154 /*
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.
157 */
158 void
159 _dyld_debug_set_error_func(
160 void (*func)(struct dyld_debug_error_data *e))
161 {
162 // do nothing
163 }
164
165
166
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)
169 {
170 const struct mach_header* mh = xprocess_read(target_task, otherAddressHeader, 0x2000);
171 if ( mh != NULL ) {
172 int i;
173 const struct segment_command *sgp =
174 (const struct segment_command *)((char*)mh + sizeof(mh));
175
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;
180 }
181 }
182 sgp = (const struct segment_command *)((char *)sgp + sgp->cmdsize);
183 }
184 free((void*)mh);
185 }
186 return 0;
187 }
188
189
190 /*
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.
195 */
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))
203 {
204 sImages = NULL;
205 sImagesCount = 0;
206 sImagesTaskPort = 0;
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 ) {
218 int i;
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);
232 }
233 }
234 }
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()
237 free((void*)infos);
238 }
239 }
240
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);
245
246 return DYLD_SUCCESS;
247 }
248
249 #endif