]> git.saurik.com Git - apple/dyld.git/blame - src/dyld_debug.c
dyld-96.2.tar.gz
[apple/dyld.git] / src / dyld_debug.c
CommitLineData
0959b6d4
A
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
25int dummy_dyld_symbol = 1;
26
0959b6d4
A
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()
44static const struct dyld_image_info* sImages = NULL;
45static uint32_t sImagesCount = 0;
46static 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
51static 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
76static 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
98static 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 */
118enum dyld_debug_return
119_dyld_debug_module_name(
120task_port_t target_task,
121unsigned long send_timeout,
122unsigned long rcv_timeout,
123boolean_t inconsistent_data_ok,
124struct dyld_debug_module module,
125char **image_name,
126unsigned long *image_nameCnt,
127char **module_name,
128unsigned 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 */
157void
158_dyld_debug_set_error_func(
159void (*func)(struct dyld_debug_error_data *e))
160{
161 // do nothing
162}
163
164
165
bac542e6 166// Examine a mach_header in another process and determine its slide
0959b6d4
A
167static ptrdiff_t slideForHeader(task_port_t target_task, const struct mach_header* otherAddressHeader)
168{
bac542e6 169 ptrdiff_t result = 0;
0959b6d4
A
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) {
bac542e6
A
179 result = (uintptr_t)mh - (uintptr_t)sgp->vmaddr;
180 break;
0959b6d4
A
181 }
182 }
183 sgp = (const struct segment_command *)((char *)sgp + sgp->cmdsize);
184 }
185 free((void*)mh);
186 }
bac542e6 187 return result;
0959b6d4
A
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 */
197enum dyld_debug_return
198_dyld_debug_subscribe_to_events(
199task_port_t target_task,
200unsigned long send_timeout,
201unsigned long rcv_timeout,
202boolean_t inconsistent_data_ok,
203void (*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