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