1/*
2 * Copyright (c) 1983, 1993, 2001
3 *      The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29#include "gprof.h"
30#include "search_list.h"
31#include "source.h"
32#include "symtab.h"
33#include "cg_arcs.h"
34#include "corefile.h"
35#include "hist.h"
36
37    /*
38     *        opcode of the `callf' instruction
39     */
40#define	CALLF	0xfe
41
42    /*
43     *        register for pc relative addressing
44     */
45#define	PC	0xf
46
47enum tahoe_opermodes
48  {
49    literal, indexed, reg, regdef, autodec, autoinc, autoincdef,
50    bytedisp, bytedispdef, worddisp, worddispdef, longdisp, longdispdef,
51    immediate, absolute, byterel, bytereldef, wordrel, wordreldef,
52    longrel, longreldef
53  };
54typedef enum tahoe_opermodes tahoe_operandenum;
55
56/*
57 * A symbol to be the child of indirect callf:
58 */
59static Sym indirectchild;
60
61static tahoe_operandenum tahoe_operandmode PARAMS ((unsigned char *));
62static char *tahoe_operandname PARAMS ((tahoe_operandenum));
63static long tahoe_operandlength PARAMS ((unsigned char *));
64static bfd_signed_vma tahoe_offset PARAMS ((unsigned char *));
65void tahoe_find_call PARAMS ((Sym *, bfd_vma, bfd_vma));
66
67static tahoe_operandenum
68tahoe_operandmode (modep)
69     unsigned char *modep;
70{
71  long usesreg = *modep & 0xf;
72
73  switch ((*modep >> 4) & 0xf)
74    {
75    case 0:
76    case 1:
77    case 2:
78    case 3:
79      return literal;
80    case 4:
81      return indexed;
82    case 5:
83      return reg;
84    case 6:
85      return regdef;
86    case 7:
87      return autodec;
88    case 8:
89      return usesreg != 0xe ? autoinc : immediate;
90    case 9:
91      return usesreg != PC ? autoincdef : absolute;
92    case 10:
93      return usesreg != PC ? bytedisp : byterel;
94    case 11:
95      return usesreg != PC ? bytedispdef : bytereldef;
96    case 12:
97      return usesreg != PC ? worddisp : wordrel;
98    case 13:
99      return usesreg != PC ? worddispdef : wordreldef;
100    case 14:
101      return usesreg != PC ? longdisp : longrel;
102    case 15:
103      return usesreg != PC ? longdispdef : longreldef;
104    }
105  /* NOTREACHED */
106  abort ();
107}
108
109static char *
110tahoe_operandname (mode)
111     tahoe_operandenum mode;
112{
113
114  switch (mode)
115    {
116    case literal:
117      return "literal";
118    case indexed:
119      return "indexed";
120    case reg:
121      return "register";
122    case regdef:
123      return "register deferred";
124    case autodec:
125      return "autodecrement";
126    case autoinc:
127      return "autoincrement";
128    case autoincdef:
129      return "autoincrement deferred";
130    case bytedisp:
131      return "byte displacement";
132    case bytedispdef:
133      return "byte displacement deferred";
134    case byterel:
135      return "byte relative";
136    case bytereldef:
137      return "byte relative deferred";
138    case worddisp:
139      return "word displacement";
140    case worddispdef:
141      return "word displacement deferred";
142    case wordrel:
143      return "word relative";
144    case wordreldef:
145      return "word relative deferred";
146    case immediate:
147      return "immediate";
148    case absolute:
149      return "absolute";
150    case longdisp:
151      return "long displacement";
152    case longdispdef:
153      return "long displacement deferred";
154    case longrel:
155      return "long relative";
156    case longreldef:
157      return "long relative deferred";
158    }
159  /* NOTREACHED */
160  abort ();
161}
162
163static long
164tahoe_operandlength (modep)
165     unsigned char *modep;
166{
167
168  switch (tahoe_operandmode (modep))
169    {
170    case literal:
171    case reg:
172    case regdef:
173    case autodec:
174    case autoinc:
175    case autoincdef:
176      return 1;
177    case bytedisp:
178    case bytedispdef:
179    case byterel:
180    case bytereldef:
181      return 2;
182    case worddisp:
183    case worddispdef:
184    case wordrel:
185    case wordreldef:
186      return 3;
187    case immediate:
188    case absolute:
189    case longdisp:
190    case longdispdef:
191    case longrel:
192    case longreldef:
193      return 5;
194    case indexed:
195      return 1 + tahoe_operandlength (modep + 1);
196    }
197  /* NOTREACHED */
198  abort ();
199}
200
201static bfd_signed_vma
202tahoe_offset (modep)
203     unsigned char *modep;
204{
205  tahoe_operandenum mode = tahoe_operandmode (modep);
206
207  ++modep;				/* skip over the mode */
208  switch (mode)
209    {
210    default:
211      fprintf (stderr, "[reladdr] not relative address\n");
212      return 0;
213    case byterel:
214      return 1 + bfd_get_signed_8 (core_bfd, modep);
215    case wordrel:
216      return 2 + bfd_get_signed_16 (core_bfd, modep);
217    case longrel:
218      return 4 + bfd_get_signed_32 (core_bfd, modep);
219    }
220}
221
222void
223tahoe_find_call (parent, p_lowpc, p_highpc)
224     Sym *parent;
225     bfd_vma p_lowpc;
226     bfd_vma p_highpc;
227{
228  unsigned char *instructp;
229  long length;
230  Sym *child;
231  tahoe_operandenum mode;
232  tahoe_operandenum firstmode;
233  bfd_vma pc, destpc;
234  static bfd_boolean inited = FALSE;
235
236  if (!inited)
237    {
238      inited = TRUE;
239      sym_init (&indirectchild);
240      indirectchild.cg.prop.fract = 1.0;
241      indirectchild.cg.cyc.head = &indirectchild;
242    }
243
244  if (core_text_space == 0)
245    {
246      return;
247    }
248  if (p_lowpc < s_lowpc)
249    {
250      p_lowpc = s_lowpc;
251    }
252  if (p_highpc > s_highpc)
253    {
254      p_highpc = s_highpc;
255    }
256  DBG (CALLDEBUG, printf ("[findcall] %s: 0x%lx to 0x%lx\n",
257			  parent->name, (unsigned long) p_lowpc,
258			  (unsigned long) p_highpc));
259  for (pc = p_lowpc; pc < p_highpc; pc += length)
260    {
261      length = 1;
262      instructp = ((unsigned char *) core_text_space
263		   + pc - core_text_sect->vma);
264      if ((*instructp & 0xff) == CALLF)
265	{
266	  /*
267	   *    maybe a callf, better check it out.
268	   *      skip the count of the number of arguments.
269	   */
270	  DBG (CALLDEBUG, printf ("[findcall]\t0x%lx:callf",
271				  (unsigned long) pc));
272	  firstmode = tahoe_operandmode (instructp + length);
273	  switch (firstmode)
274	    {
275	    case literal:
276	    case immediate:
277	      break;
278	    default:
279	      goto botched;
280	    }
281	  length += tahoe_operandlength (instructp + length);
282	  mode = tahoe_operandmode (instructp + length);
283	  DBG (CALLDEBUG,
284	       printf ("\tfirst operand is %s", tahoe_operandname (firstmode));
285	       printf ("\tsecond operand is %s\n", tahoe_operandname (mode));
286	    );
287	  switch (mode)
288	    {
289	    case regdef:
290	    case bytedispdef:
291	    case worddispdef:
292	    case longdispdef:
293	    case bytereldef:
294	    case wordreldef:
295	    case longreldef:
296	      /*
297	       *    indirect call: call through pointer
298	       *      either  *d(r)   as a parameter or local
299	       *              (r)     as a return value
300	       *              *f      as a global pointer
301	       *      [are there others that we miss?,
302	       *       e.g. arrays of pointers to functions???]
303	       */
304	      arc_add (parent, &indirectchild, (unsigned long) 0);
305	      length += tahoe_operandlength (instructp + length);
306	      continue;
307	    case byterel:
308	    case wordrel:
309	    case longrel:
310	      /*
311	       *    regular pc relative addressing
312	       *      check that this is the address of
313	       *      a function.
314	       */
315	      destpc = pc + tahoe_offset (instructp + length);
316	      if (destpc >= s_lowpc && destpc <= s_highpc)
317		{
318		  child = sym_lookup (&symtab, destpc);
319		  DBG (CALLDEBUG,
320		       printf ("[findcall]\tdestpc 0x%lx",
321			       (unsigned long) destpc);
322		       printf (" child->name %s", child->name);
323		       printf (" child->addr 0x%lx\n",
324			       (unsigned long) child->addr);
325		    );
326		  if (child->addr == destpc)
327		    {
328		      /*
329		       *    a hit
330		       */
331		      arc_add (parent, child, (unsigned long) 0);
332		      length += tahoe_operandlength (instructp + length);
333		      continue;
334		    }
335		  goto botched;
336		}
337	      /*
338	       *    else:
339	       *      it looked like a callf,
340	       *      but it wasn't to anywhere.
341	       */
342	      goto botched;
343	    default:
344	    botched:
345	      /*
346	       *    something funny going on.
347	       */
348	      DBG (CALLDEBUG, printf ("[findcall]\tbut it's a botch\n"));
349	      length = 1;
350	      continue;
351	    }
352	}
353    }
354}
355