1/*
2 * tkMacOSXDebug.c --
3 *
4 *	Implementation of Macintosh specific functions for debugging MacOS events,
5 *	regions, etc...
6 *
7 * Copyright 2001, Apple Computer, Inc.
8 * Copyright (c) 2006-2007 Daniel A. Steffen <das@users.sourceforge.net>
9 *
10 * See the file "license.terms" for information on usage and redistribution of
11 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 *	The following terms apply to all files originating from Apple
14 *	Computer, Inc. ("Apple") and associated with the software
15 *	unless explicitly disclaimed in individual files.
16 *
17 *
18 *	Apple hereby grants permission to use, copy, modify,
19 *	distribute, and license this software and its documentation
20 *	for any purpose, provided that existing copyright notices are
21 *	retained in all copies and that this notice is included
22 *	verbatim in any distributions. No written agreement, license,
23 *	or royalty fee is required for any of the authorized
24 *	uses. Modifications to this software may be copyrighted by
25 *	their authors and need not follow the licensing terms
26 *	described here, provided that the new terms are clearly
27 *	indicated on the first page of each file where they apply.
28 *
29 *
30 *	IN NO EVENT SHALL APPLE, THE AUTHORS OR DISTRIBUTORS OF THE
31 *	SOFTWARE BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL,
32 *	INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
33 *	THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
34 *	EVEN IF APPLE OR THE AUTHORS HAVE BEEN ADVISED OF THE
35 *	POSSIBILITY OF SUCH DAMAGE.  APPLE, THE AUTHORS AND
36 *	DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
37 *	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
38 *	FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.	 THIS
39 *	SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND APPLE,THE
40 *	AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
41 *	MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
42 *
43 *	GOVERNMENT USE: If you are acquiring this software on behalf
44 *	of the U.S. government, the Government shall have only
45 *	"Restricted Rights" in the software and related documentation
46 *	as defined in the Federal Acquisition Regulations (FARs) in
47 *	Clause 52.227.19 (c) (2).  If you are acquiring the software
48 *	on behalf of the Department of Defense, the software shall be
49 *	classified as "Commercial Computer Software" and the
50 *	Government shall have only "Restricted Rights" as defined in
51 *	Clause 252.227-7013 (c) (1) of DFARs.  Notwithstanding the
52 *	foregoing, the authors grant the U.S. Government and others
53 *	acting in its behalf permission to use and distribute the
54 *	software in accordance with the terms specified in this
55 *	license.
56 *
57 * RCS: @(#) $Id: tkMacOSXDebug.c,v 1.2.2.14 2007/11/09 06:26:55 das Exp $
58 */
59
60#include "tkMacOSXPrivate.h"
61#include "tkMacOSXDebug.h"
62
63#ifdef TK_MAC_DEBUG
64
65typedef struct {
66    EventKind kind;
67    const char * name;
68} MyEventName;
69
70typedef struct {
71    EventClass c;
72    MyEventName * names;
73} MyEventNameList;
74
75static MyEventName windowEventNames [] = {
76 {  kEventWindowUpdate,"Update"},
77 {  kEventWindowDrawContent,"DrawContent"},
78 {  kEventWindowActivated,"Activated"},
79 {  kEventWindowDeactivated,"Deactivated"},
80 {  kEventWindowGetClickActivation,"GetClickActivation"},
81 {  kEventWindowShowing,"Showing"},
82 {  kEventWindowHiding,"Hiding"},
83 {  kEventWindowShown,"Shown"},
84 {  kEventWindowHidden,"Hidden"},
85 {  kEventWindowBoundsChanging,"BoundsChanging"},
86 {  kEventWindowBoundsChanged,"BoundsChanged"},
87 {  kEventWindowResizeStarted,"ResizeStarted"},
88 {  kEventWindowResizeCompleted,"ResizeCompleted"},
89 {  kEventWindowDragStarted,"DragStarted"},
90 {  kEventWindowDragCompleted,"DragCompleted"},
91 {  kEventWindowClickDragRgn,"ClickDragRgn"},
92 {  kEventWindowClickResizeRgn,"ClickResizeRgn"},
93 {  kEventWindowClickCollapseRgn,"ClickCollapseRgn"},
94 {  kEventWindowClickCloseRgn,"ClickCloseRgn"},
95 {  kEventWindowClickZoomRgn,"ClickZoomRgn"},
96 {  kEventWindowClickContentRgn,"ClickContentRgn"},
97 {  kEventWindowClickProxyIconRgn,"ClickProxyIconRgn"},
98 {  kEventWindowCursorChange,"CursorChange" },
99 {  kEventWindowCollapse,"Collapse"},
100 {  kEventWindowCollapsed,"Collapsed"},
101 {  kEventWindowCollapseAll,"CollapseAll"},
102 {  kEventWindowExpand,"Expand"},
103 {  kEventWindowExpanded,"Expanded"},
104 {  kEventWindowExpandAll,"ExpandAll"},
105 {  kEventWindowCollapse,"Collapse"},
106 {  kEventWindowClose,"Close"},
107 {  kEventWindowClosed,"Closed"},
108 {  kEventWindowCloseAll,"CloseAll"},
109 {  kEventWindowZoom,"Zoom"},
110 {  kEventWindowZoomed,"Zoomed"},
111 {  kEventWindowZoomAll,"ZoomAll"},
112 {  kEventWindowContextualMenuSelect,"ContextualMenuSelect"},
113 {  kEventWindowPathSelect,"PathSelect"},
114 {  kEventWindowGetIdealSize,"GetIdealSize"},
115 {  kEventWindowGetMinimumSize,"GetMinimumSize"},
116 {  kEventWindowGetMaximumSize,"GetMaximumSize"},
117 {  kEventWindowConstrain,"Constrain"},
118 {  kEventWindowHandleContentClick,"HandleContentClick"},
119 {  kEventWindowProxyBeginDrag,"ProxyBeginDra}"},
120 {  kEventWindowProxyEndDrag,"ProxyEndDrag"},
121 {  kEventWindowFocusAcquired,"FocusAcquired"},
122 {  kEventWindowFocusRelinquish,"FocusRelinquish"},
123 {  kEventWindowDrawFrame,"DrawFrame"},
124 {  kEventWindowDrawPart,"DrawPart"},
125 {  kEventWindowGetRegion,"GetRegion"},
126 {  kEventWindowHitTest,"HitTest"},
127 {  kEventWindowInit,"Init"},
128 {  kEventWindowDispose,"Dispose"},
129 {  kEventWindowDragHilite,"DragHilite"},
130 {  kEventWindowModified,"Modified"},
131 {  kEventWindowSetupProxyDragImage,"SetupProxyDragImage"},
132 {  kEventWindowStateChanged,"StateChanged"},
133 {  kEventWindowMeasureTitle,"MeasureTitle"},
134 {  kEventWindowDrawGrowBox,"DrawGrowBox"},
135 {  kEventWindowGetGrowImageRegion,"GetGrowImageRegion"},
136 {  kEventWindowPaint,"Paint"},
137 { 0, NULL },
138};
139
140static MyEventName mouseEventNames [] = {
141 {  kEventMouseMoved, "Moved"},
142 {  kEventMouseUp, "Up"},
143 {  kEventMouseDown, "Down"},
144 {  kEventMouseDragged, "Dragged"},
145 {  kEventMouseWheelMoved, "WheelMoved"},
146 { 0, NULL}
147};
148
149static MyEventName keyboardEventNames [] = {
150 { kEventRawKeyDown, "Down"},
151 { kEventRawKeyRepeat, "Repeat"},
152 { kEventRawKeyUp, "Up"},
153 { kEventRawKeyModifiersChanged, "ModifiersChanged"},
154 { kEventHotKeyPressed, "HotKeyPressed"},
155 { kEventHotKeyReleased, "HotKeyReleased"},
156 { 0, NULL}
157};
158
159static MyEventName appEventNames [] = {
160 { kEventAppActivated, "Activated"},
161 { kEventAppDeactivated, "Deactivated"},
162 { kEventAppQuit, "Quit"},
163 { kEventAppLaunchNotification, "LaunchNotification"},
164 { kEventAppLaunched, "Launched"},
165 { kEventAppTerminated, "Terminated"},
166 { kEventAppFrontSwitched, "FrontSwitched"},
167 { 0, NULL}
168};
169
170static MyEventName menuEventNames [] = {
171 { kEventMenuBeginTracking, "BeginTracking"},
172 { kEventMenuEndTracking, "EndTracking"},
173 { kEventMenuChangeTrackingMode, "ChangeTrackingMode"},
174 { kEventMenuOpening, "Opening"},
175 { kEventMenuClosed, "Closed"},
176 { kEventMenuTargetItem, "TargetItem"},
177 { kEventMenuMatchKey, "MatchKey"},
178 { kEventMenuEnableItems, "EnableItems"},
179 { kEventMenuDispose, "Dispose"},
180 { 0, NULL }
181};
182
183static MyEventName controlEventNames [] = {
184 { kEventControlInitialize, "Initialize" },
185 { kEventControlDispose, "Dispose" },
186 { kEventControlGetOptimalBounds, "GetOptimalBounds" },
187 { kEventControlHit, "Hit" },
188 { kEventControlSimulateHit, "SimulateHit" },
189 { kEventControlHitTest, "HitTest" },
190 { kEventControlDraw, "Draw" },
191 { kEventControlApplyBackground, "ApplyBackground" },
192 { kEventControlApplyTextColor, "ApplyTextColor" },
193 { kEventControlSetFocusPart, "SetFocusPart" },
194 { kEventControlGetFocusPart, "GetFocusPart" },
195 { kEventControlActivate, "Activate" },
196 { kEventControlDeactivate, "Deactivate" },
197 { kEventControlSetCursor, "SetCursor" },
198 { kEventControlContextualMenuClick, "ContextualMenuClick" },
199 { kEventControlClick, "Click" },
200 { kEventControlTrack, "Track" },
201 { kEventControlGetScrollToHereStartPoint, "GetScrollToHereStartPoint" },
202 { kEventControlGetIndicatorDragConstraint, "GetIndicatorDragConstraint" },
203 { kEventControlIndicatorMoved, "IndicatorMoved" },
204 { kEventControlGhostingFinished, "GhostingFinished" },
205 { kEventControlGetActionProcPart, "GetActionProcPart" },
206 { kEventControlGetPartRegion, "GetPartRegion" },
207 { kEventControlGetPartBounds, "GetPartBounds" },
208 { kEventControlSetData, "SetData" },
209 { kEventControlGetData, "GetData" },
210 { kEventControlValueFieldChanged, "ValueFieldChanged" },
211 { kEventControlAddedSubControl, "AddedSubControl" },
212 { kEventControlRemovingSubControl, "RemovingSubControl" },
213 { kEventControlBoundsChanged, "BoundsChanged" },
214 { kEventControlOwningWindowChanged, "OwningWindowChanged" },
215 { kEventControlArbitraryMessage, "ArbitraryMessage" },
216 { 0, NULL }
217};
218
219static MyEventName commandEventNames [] = {
220 { kEventCommandProcess, "Process" },
221 { kEventCommandUpdateStatus, "UpdateStatus" },
222 { 0, NULL }
223};
224
225static MyEventNameList eventNameList [] = {
226 { kEventClassWindow, windowEventNames },
227 { kEventClassMouse, mouseEventNames },
228 { kEventClassKeyboard, keyboardEventNames },
229 { kEventClassApplication, appEventNames },
230 { kEventClassMenu, menuEventNames },
231 { kEventClassControl, controlEventNames },
232 { kEventClassCommand, commandEventNames },
233 { 0, NULL}
234};
235
236#ifdef TK_MACOSXDEBUG_UNUSED
237static MyEventName classicEventNames [] = {
238 { nullEvent,"nullEvent" },
239 { mouseDown,"mouseDown" },
240 { mouseUp,"mouseUp" },
241 { keyDown,"keyDown" },
242 { keyUp,"keyUp" },
243 { autoKey,"autoKey" },
244 { updateEvt,"updateEvt" },
245 { diskEvt,"diskEvt" },
246 { activateEvt,"activateEvt" },
247 { osEvt,"osEvt" },
248 { kHighLevelEvent,"kHighLevelEvent" },
249 { 0, NULL }
250};
251#endif /* TK_MACOSXDEBUG_UNUSED */
252
253MODULE_SCOPE char *
254TkMacOSXCarbonEventToAscii(EventRef eventRef)
255{
256    EventClass eventClass;
257    EventKind eventKind;
258    MyEventNameList * list = eventNameList;
259    MyEventName * names = NULL;
260    static char str[256];
261    char *buf = str;
262    int *iPtr = (int*)str;
263    int found = 0;
264
265    eventClass = GetEventClass(eventRef);
266    eventKind = GetEventKind(eventRef);
267
268    *iPtr = eventClass;
269    buf[4] = 0;
270    strcat(buf, " ");
271    buf += strlen(buf);
272    while (list->names && (!names) ) {
273	if (eventClass == list->c) {
274	    names = list -> names;
275	} else {
276	    list++;
277	}
278    }
279   while (names && names->name) {
280       if (eventKind == names->kind) {
281	   snprintf(buf, 250, "%-20s", names->name);
282	   break;
283       } else {
284	   names++;
285       }
286    }
287    if (!found) {
288	snprintf(buf, 250, "%-20d", eventKind);
289    }
290    return str;
291}
292
293#ifdef TK_MACOSXDEBUG_UNUSED
294MODULE_SCOPE char *
295TkMacOSXCarbonEventKindToAscii(EventRef eventRef, char * buf )
296{
297   EventClass eventClass;
298   EventKind  eventKind;
299   MyEventNameList * list = eventNameList;
300   MyEventName	   * names = NULL;
301   int		     found = 0;
302   eventClass = GetEventClass(eventRef);
303   eventKind = GetEventKind(eventRef);
304   while (list->names && (!names) ) {
305       if (eventClass == list -> c) {
306	   names = list -> names;
307       } else {
308	   list++;
309       }
310   }
311   if (names) {
312       found = 0;
313       while ( names->name && !found ) {
314	   if (eventKind == names->kind) {
315	       sprintf(buf,"%s",names->name);
316	       found = 1;
317	   } else {
318	       names++;
319	   }
320       }
321    }
322    if (!found) {
323	sprintf ( buf,"%d", eventKind );
324     } else {
325	sprintf ( buf,"%d", eventKind );
326     }
327     return buf;
328}
329
330MODULE_SCOPE char *
331TkMacOSXClassicEventToAscii(EventRecord * eventPtr, char * buf )
332{
333    MyEventName	    * names = NULL;
334    int found = 0;
335    names = classicEventNames;
336    while ( names -> name && !found )
337	if (eventPtr->what == names->kind) {
338	    int * iPtr;
339	    char cBuf[8];
340	    iPtr=(int *) &cBuf;
341	    *iPtr = eventPtr->message;
342	    cBuf[4] = 0;
343	    sprintf(buf, "%-16s %08x %04x %s", names->name,
344		    (int) eventPtr->message,
345		    eventPtr->modifiers,
346		    cBuf);
347	    found = 1;
348	} else {
349	  names++;
350	}
351    if (!found) {
352	       sprintf(buf,"%-16d %08x %08x, %s",
353		       eventPtr->what, (int) eventPtr->message,
354		       eventPtr->modifiers, buf);
355    }
356    return buf;
357
358}
359
360MODULE_SCOPE void
361TkMacOSXPrintPoint(char * tag, Point * p )
362{
363    TkMacOSXDbgMsg("%s %4d %4d", tag,p->h,p->v );
364}
365
366MODULE_SCOPE void
367TkMacOSXPrintRect(char * tag, Rect * r )
368{
369    TkMacOSXDbgMsg("%s %4d %4d %4d %4d (%dx%d)",
370	tag, r->left, r->top, r->right, r->bottom,
371	r->right - r->left + 1, r->bottom - r->top + 1);
372}
373
374MODULE_SCOPE void
375TkMacOSXPrintRegion(char * tag, RgnHandle rgn )
376{
377    Rect r;
378    GetRegionBounds(rgn,&r);
379    TkMacOSXPrintRect(tag,&r);
380}
381
382MODULE_SCOPE void
383TkMacOSXPrintWindowTitle(char * tag, WindowRef window )
384{
385    Str255 title;
386    GetWTitle(window,title);
387    title [title[0] + 1] = 0;
388    TkMacOSXDbgMsg("%s %s", tag, title +1 );
389}
390
391typedef struct {
392 int	msg;
393 char * name;
394} MsgName;
395
396static MsgName msgNames [] = {
397    { kMenuDrawMsg,	  "Draw"},
398    { kMenuSizeMsg,	  "Size"},
399    { kMenuPopUpMsg,	  "PopUp"},
400    { kMenuCalcItemMsg,	  "CalcItem" },
401    { kMenuThemeSavvyMsg, "ThemeSavvy"},
402    { kMenuInitMsg,	  "Init" },
403    { kMenuDisposeMsg,	  "Dispose" },
404    { kMenuFindItemMsg,	  "FindItem" },
405    { kMenuHiliteItemMsg, "HiliteItem" },
406    { kMenuDrawItemsMsg,  "DrawItems" },
407    { -1, NULL }
408};
409
410MODULE_SCOPE char *
411TkMacOSXMenuMessageToAscii(int msg, char * s)
412{
413    MsgName * msgNamePtr;
414    for (msgNamePtr = msgNames;msgNamePtr->name;) {
415	if (msgNamePtr->msg == msg) {
416	   strcpy(s,msgNamePtr->name);
417	   return s;
418	} else {
419	    msgNamePtr++;
420	}
421    }
422    sprintf(s,"unknown : %d", msg );
423    return s;
424}
425
426static MsgName trackingNames [] = {
427    { kMouseTrackingMousePressed  , "MousePressed  " },
428    { kMouseTrackingMouseReleased , "MouseReleased " },
429    { kMouseTrackingMouseExited	  , "MouseExited   " },
430    { kMouseTrackingMouseEntered  , "MouseEntered  " },
431    { kMouseTrackingMouseMoved	  , "MouseMoved	   " },
432    { kMouseTrackingKeyModifiersChanged, "KeyModifiersChanged" },
433    { kMouseTrackingUserCancelled , "UserCancelled " },
434    { kMouseTrackingTimedOut	  , "TimedOut	   " },
435    { -1, NULL }
436};
437
438MODULE_SCOPE char *
439TkMacOSXMouseTrackingResultToAscii(MouseTrackingResult r, char * buf)
440{
441    MsgName * namePtr;
442    for (namePtr = trackingNames; namePtr->name; namePtr++) {
443	if (namePtr->msg == r) {
444	    strcpy(buf, namePtr->name);
445	    return buf;
446	}
447    }
448    sprintf(buf, "Unknown mouse tracking result : %d", r);
449    return buf;
450}
451#endif /* TK_MACOSXDEBUG_UNUSED */
452
453MODULE_SCOPE void
454TkMacOSXDebugFlashRegion(
455    Drawable d,
456    HIShapeRef rgn)
457{
458    TkMacOSXInitNamedDebugSymbol(HIToolbox, int, QDDebugFlashRegion,
459	    CGrafPtr port, RgnHandle region);
460    CFShow(rgn);
461    if (d && rgn && QDDebugFlashRegion && !HIShapeIsEmpty(rgn)) {
462	CGrafPtr port = TkMacOSXGetDrawablePort(d);
463
464	if (port) {
465	    static RgnHandle qdRgn = NULL;
466
467	    if (!qdRgn) {
468		qdRgn = NewRgn();
469	    }
470	    ChkErr(HIShapeGetAsQDRgn, rgn, qdRgn);
471
472	    /*
473	     * Carbon-internal region flashing SPI (c.f. Technote 2124)
474	     */
475
476	    QDDebugFlashRegion(port, qdRgn);
477	    SetEmptyRgn(qdRgn);
478	}
479    }
480}
481
482#include <mach-o/dyld.h>
483#include <mach-o/nlist.h>
484
485/*
486 *----------------------------------------------------------------------
487 *
488 * TkMacOSXGetNamedDebugSymbol --
489 *
490 *
491 *	Dynamically acquire address of a named symbol from a loaded
492 *	dynamic library, so that we can use API that may not be
493 *	available on all OS versions.
494 *	For debugging purposes, if we cannot find the symbol with the
495 *	usual dynamic library APIs, we manually walk the symbol table
496 *	of the loaded library. This allows access to unexported
497 *	symbols such as private_extern internal debugging functions.
498 *	If module is NULL or the empty string, search all loaded
499 *	libraries (could be very expensive and should be avoided).
500 *
501 *	THIS FUCTION IS ONLY TO BE USED FOR DEBUGGING PURPOSES, IT MAY
502 *	BREAK UNEXPECTEDLY IN THE FUTURE !
503 *
504 * Results:
505 *	Address of given symbol or NULL if unavailable.
506 *
507 * Side effects:
508 *	None.
509 *
510 *----------------------------------------------------------------------
511 */
512
513MODULE_SCOPE void *
514TkMacOSXGetNamedDebugSymbol(
515    const char* module,
516    const char* symbol)
517{
518    void* addr = TkMacOSXGetNamedSymbol(module, symbol);
519#ifndef __LP64__
520    if (!addr) {
521	const struct mach_header *mh = NULL;
522	uint32_t i, n = _dyld_image_count();
523	size_t module_len = 0;
524
525	if (module && *module) {
526	    module_len = strlen(module);
527	}
528	for (i = 0; i < n; i++) {
529	    if (module && *module) {
530		/* Find image with given module name */
531		char *name;
532		const char *path = _dyld_get_image_name(i);
533
534		if (!path) {
535		    continue;
536		}
537		name = strrchr(path, '/') + 1;
538		if (strncmp(name, module, module_len) != 0) {
539		    continue;
540		}
541	    }
542	    mh = _dyld_get_image_header(i);
543	    if (mh) {
544		struct load_command *lc;
545		struct symtab_command *st = NULL;
546		struct segment_command *sg = NULL;
547		uint32_t j, m, nsect = 0, txtsectx = 0;
548
549		lc = (struct load_command*)((const char*) mh +
550			sizeof(struct mach_header));
551		m = mh->ncmds;
552		for (j = 0; j < m; j++) {
553		    /* Find symbol table and index of __text section */
554		    if (lc->cmd == LC_SEGMENT) {
555			/* Find last segment before symbol table */
556			sg = (struct segment_command*) lc;
557			if (!txtsectx) {
558			    /* Count total sections until (__TEXT, __text) */
559			    uint32_t k, ns = sg->nsects;
560
561			    if (strcmp(sg->segname, SEG_TEXT) == 0) {
562				struct section *s = (struct section *)(
563					(char *)sg +
564					sizeof(struct segment_command));
565
566				for(k = 0; k < ns; k++) {
567				    if (strcmp(s->sectname, SECT_TEXT) == 0) {
568					txtsectx = nsect+k+1;
569					break;
570				    }
571				    s++;
572				}
573			    }
574			    nsect += ns;
575			}
576		    } else if (!st && lc->cmd == LC_SYMTAB) {
577			st = (struct symtab_command*) lc;
578			break;
579		    }
580		    lc = (struct load_command *)((char *) lc + lc->cmdsize);
581		}
582		if (st && sg && txtsectx) {
583		    intptr_t base, slide = _dyld_get_image_vmaddr_slide(i);
584		    char *strings;
585		    struct nlist *sym;
586		    uint32_t strsize = st->strsize;
587		    int32_t strx;
588
589		    /* Offset file positions by difference to actual position
590		       in memory of last segment before symbol table: */
591		    base = (intptr_t) sg->vmaddr + slide - sg->fileoff;
592		    strings = (char*)(base + st->stroff);
593		    sym = (struct nlist*)(base + st->symoff);
594		    m = st->nsyms;
595		    for (j = 0; j < m; j++) {
596			/* Find symbol with given name in __text section */
597			strx = sym->n_un.n_strx;
598			if ((sym->n_type & N_TYPE) == N_SECT &&
599				sym->n_sect == txtsectx &&
600				strx > 0 && (uint32_t) strx < strsize &&
601				strcmp(strings + strx, symbol) == 0) {
602			    addr = (char*) sym->n_value + slide;
603			    break;
604			}
605			sym++;
606		    }
607		}
608	    }
609	    if (module && *module) {
610		/* If given a module name, only search corresponding image */
611		break;
612	    }
613	}
614    }
615#endif /* __LP64__ */
616    return addr;
617}
618
619#endif /* TK_MAC_DEBUG */
620