]> git.saurik.com Git - apple/dyld.git/blob - src/dyld_debug.c
632efab9fbe6d579d6e479c77abeb7653f48e974
[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 <stdlib.h>
28
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).
32 //
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.
37 //
38 #if __ppc__
39
40 #include "mach-o/dyld_debug.h"
41 #include "mach-o/dyld_gdb.h"
42
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;
47
48
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)
52 {
53 void* result = NULL;
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;
57 uint8_t* local_start;
58 uint32_t local_len;
59 kern_return_t r = vm_read(
60 target_task,
61 page_address,
62 page_size,
63 (vm_offset_t*)&local_start,
64 &local_len);
65 if ( r == KERN_SUCCESS ) {
66 result = malloc(len);
67 if ( result != NULL )
68 memcpy(result, &local_start[(uint32_t)address - page_address], len);
69 vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_len);
70 }
71 return result;
72 }
73
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)
77 {
78 char* result = NULL;
79 mach_vm_address_t page_address = (uint32_t)address & (-4096);
80 mach_vm_size_t page_size = 0x2000; // always read two pages
81 uint8_t* local_start;
82 uint32_t local_len;
83 kern_return_t r = vm_read(
84 target_task,
85 page_address,
86 page_size,
87 (vm_offset_t*)&local_start,
88 &local_len);
89 if ( r == KERN_SUCCESS ) {
90 const char* str = (char*)&local_start[(uint32_t)address - page_address];
91 return str;
92 }
93 return result;
94 }
95
96
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()
99 {
100 static const struct dyld_all_image_infos* (*p)() = NULL;
101
102 if ( p == NULL )
103 _dyld_func_lookup("__dyld_get_all_image_infos", (void**)&p);
104
105 if ( p != NULL )
106 return p();
107 else
108 return NULL;
109 }
110
111
112
113 /*
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.
117 */
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,
125 char **image_name,
126 unsigned long *image_nameCnt,
127 char **module_name,
128 unsigned long *module_nameCnt)
129 {
130 // examine sImage* info set up by _dyld_debug_subscribe_to_events()
131 if ( sImagesTaskPort == target_task ) {
132 unsigned int i;
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);
140 *module_name = NULL;
141 *module_nameCnt = 0;
142 return DYLD_SUCCESS;
143 }
144 }
145 }
146 }
147
148 // not supported
149 return DYLD_INVALID_ARGUMENTS;
150 }
151
152
153 /*
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.
156 */
157 void
158 _dyld_debug_set_error_func(
159 void (*func)(struct dyld_debug_error_data *e))
160 {
161 // do nothing
162 }
163
164
165
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)
168 {
169 ptrdiff_t result = 0;
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 result = (uintptr_t)mh - (uintptr_t)sgp->vmaddr;
180 break;
181 }
182 }
183 sgp = (const struct segment_command *)((char *)sgp + sgp->cmdsize);
184 }
185 free((void*)mh);
186 }
187 return result;
188 }
189
190
191 /*
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.
196 */
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))
204 {
205 sImages = NULL;
206 sImagesCount = 0;
207 sImagesTaskPort = 0;
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 ) {
219 int i;
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);
233 }
234 }
235 }
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()
238 free((void*)infos);
239 }
240 }
241
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);
246
247 return DYLD_SUCCESS;
248 }
249
250 #endif