]> git.saurik.com Git - apple/dyld.git/blame - src/dyld_debug.c
dyld-46.12.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
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()
45static const struct dyld_image_info* sImages = NULL;
46static uint32_t sImagesCount = 0;
47static 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
52static 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
77static 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
99static 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 */
119enum dyld_debug_return
120_dyld_debug_module_name(
121task_port_t target_task,
122unsigned long send_timeout,
123unsigned long rcv_timeout,
124boolean_t inconsistent_data_ok,
125struct dyld_debug_module module,
126char **image_name,
127unsigned long *image_nameCnt,
128char **module_name,
129unsigned 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 */
158void
159_dyld_debug_set_error_func(
160void (*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
168static 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 */
196enum dyld_debug_return
197_dyld_debug_subscribe_to_events(
198task_port_t target_task,
199unsigned long send_timeout,
200unsigned long rcv_timeout,
201boolean_t inconsistent_data_ok,
202void (*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