xref: /openbsd/gnu/usr.bin/binutils-2.17/gprof/vax.c (revision 3d8817e4)
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 `calls' instruction
39*3d8817e4Smiod      */
40*3d8817e4Smiod #define	CALLS	0xfb
41*3d8817e4Smiod 
42*3d8817e4Smiod     /*
43*3d8817e4Smiod      *        register for pc relative addressing
44*3d8817e4Smiod      */
45*3d8817e4Smiod #define	PC	0xf
46*3d8817e4Smiod 
47*3d8817e4Smiod enum 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 opermodes operandenum;
55*3d8817e4Smiod 
56*3d8817e4Smiod /* *INDENT-OFF* */
57*3d8817e4Smiod /* Here to document only.  We can't use this when cross compiling as
58*3d8817e4Smiod    the bitfield layout might not be the same as native.
59*3d8817e4Smiod 
60*3d8817e4Smiod    struct modebyte
61*3d8817e4Smiod      {
62*3d8817e4Smiod        unsigned int regfield:4;
63*3d8817e4Smiod        unsigned int modefield:4;
64*3d8817e4Smiod      };
65*3d8817e4Smiod */
66*3d8817e4Smiod /* *INDENT-ON* */
67*3d8817e4Smiod 
68*3d8817e4Smiod /*
69*3d8817e4Smiod  * A symbol to be the child of indirect calls:
70*3d8817e4Smiod  */
71*3d8817e4Smiod static Sym indirectchild;
72*3d8817e4Smiod 
73*3d8817e4Smiod static operandenum vax_operandmode (unsigned char *);
74*3d8817e4Smiod static char *vax_operandname (operandenum);
75*3d8817e4Smiod static long vax_operandlength (unsigned char *);
76*3d8817e4Smiod static bfd_signed_vma vax_offset (unsigned char *);
77*3d8817e4Smiod void vax_find_call (Sym *, bfd_vma, bfd_vma);
78*3d8817e4Smiod 
79*3d8817e4Smiod static operandenum
vax_operandmode(unsigned char * modep)80*3d8817e4Smiod vax_operandmode (unsigned char *modep)
81*3d8817e4Smiod {
82*3d8817e4Smiod   int usesreg = *modep & 0xf;
83*3d8817e4Smiod 
84*3d8817e4Smiod   switch ((*modep >> 4) & 0xf)
85*3d8817e4Smiod     {
86*3d8817e4Smiod     case 0:
87*3d8817e4Smiod     case 1:
88*3d8817e4Smiod     case 2:
89*3d8817e4Smiod     case 3:
90*3d8817e4Smiod       return literal;
91*3d8817e4Smiod     case 4:
92*3d8817e4Smiod       return indexed;
93*3d8817e4Smiod     case 5:
94*3d8817e4Smiod       return reg;
95*3d8817e4Smiod     case 6:
96*3d8817e4Smiod       return regdef;
97*3d8817e4Smiod     case 7:
98*3d8817e4Smiod       return autodec;
99*3d8817e4Smiod     case 8:
100*3d8817e4Smiod       return usesreg != PC ? autoinc : immediate;
101*3d8817e4Smiod     case 9:
102*3d8817e4Smiod       return usesreg != PC ? autoincdef : absolute;
103*3d8817e4Smiod     case 10:
104*3d8817e4Smiod       return usesreg != PC ? bytedisp : byterel;
105*3d8817e4Smiod     case 11:
106*3d8817e4Smiod       return usesreg != PC ? bytedispdef : bytereldef;
107*3d8817e4Smiod     case 12:
108*3d8817e4Smiod       return usesreg != PC ? worddisp : wordrel;
109*3d8817e4Smiod     case 13:
110*3d8817e4Smiod       return usesreg != PC ? worddispdef : wordreldef;
111*3d8817e4Smiod     case 14:
112*3d8817e4Smiod       return usesreg != PC ? longdisp : longrel;
113*3d8817e4Smiod     case 15:
114*3d8817e4Smiod       return usesreg != PC ? longdispdef : longreldef;
115*3d8817e4Smiod     }
116*3d8817e4Smiod   /* NOTREACHED */
117*3d8817e4Smiod   abort ();
118*3d8817e4Smiod }
119*3d8817e4Smiod 
120*3d8817e4Smiod static char *
vax_operandname(operandenum mode)121*3d8817e4Smiod vax_operandname (operandenum mode)
122*3d8817e4Smiod {
123*3d8817e4Smiod 
124*3d8817e4Smiod   switch (mode)
125*3d8817e4Smiod     {
126*3d8817e4Smiod     case literal:
127*3d8817e4Smiod       return "literal";
128*3d8817e4Smiod     case indexed:
129*3d8817e4Smiod       return "indexed";
130*3d8817e4Smiod     case reg:
131*3d8817e4Smiod       return "register";
132*3d8817e4Smiod     case regdef:
133*3d8817e4Smiod       return "register deferred";
134*3d8817e4Smiod     case autodec:
135*3d8817e4Smiod       return "autodecrement";
136*3d8817e4Smiod     case autoinc:
137*3d8817e4Smiod       return "autoincrement";
138*3d8817e4Smiod     case autoincdef:
139*3d8817e4Smiod       return "autoincrement deferred";
140*3d8817e4Smiod     case bytedisp:
141*3d8817e4Smiod       return "byte displacement";
142*3d8817e4Smiod     case bytedispdef:
143*3d8817e4Smiod       return "byte displacement deferred";
144*3d8817e4Smiod     case byterel:
145*3d8817e4Smiod       return "byte relative";
146*3d8817e4Smiod     case bytereldef:
147*3d8817e4Smiod       return "byte relative deferred";
148*3d8817e4Smiod     case worddisp:
149*3d8817e4Smiod       return "word displacement";
150*3d8817e4Smiod     case worddispdef:
151*3d8817e4Smiod       return "word displacement deferred";
152*3d8817e4Smiod     case wordrel:
153*3d8817e4Smiod       return "word relative";
154*3d8817e4Smiod     case wordreldef:
155*3d8817e4Smiod       return "word relative deferred";
156*3d8817e4Smiod     case immediate:
157*3d8817e4Smiod       return "immediate";
158*3d8817e4Smiod     case absolute:
159*3d8817e4Smiod       return "absolute";
160*3d8817e4Smiod     case longdisp:
161*3d8817e4Smiod       return "long displacement";
162*3d8817e4Smiod     case longdispdef:
163*3d8817e4Smiod       return "long displacement deferred";
164*3d8817e4Smiod     case longrel:
165*3d8817e4Smiod       return "long relative";
166*3d8817e4Smiod     case longreldef:
167*3d8817e4Smiod       return "long relative deferred";
168*3d8817e4Smiod     }
169*3d8817e4Smiod   /* NOTREACHED */
170*3d8817e4Smiod   abort ();
171*3d8817e4Smiod }
172*3d8817e4Smiod 
173*3d8817e4Smiod static long
vax_operandlength(unsigned char * modep)174*3d8817e4Smiod vax_operandlength (unsigned char *modep)
175*3d8817e4Smiod {
176*3d8817e4Smiod 
177*3d8817e4Smiod   switch (vax_operandmode (modep))
178*3d8817e4Smiod     {
179*3d8817e4Smiod     case literal:
180*3d8817e4Smiod     case reg:
181*3d8817e4Smiod     case regdef:
182*3d8817e4Smiod     case autodec:
183*3d8817e4Smiod     case autoinc:
184*3d8817e4Smiod     case autoincdef:
185*3d8817e4Smiod       return 1;
186*3d8817e4Smiod     case bytedisp:
187*3d8817e4Smiod     case bytedispdef:
188*3d8817e4Smiod     case byterel:
189*3d8817e4Smiod     case bytereldef:
190*3d8817e4Smiod       return 2;
191*3d8817e4Smiod     case worddisp:
192*3d8817e4Smiod     case worddispdef:
193*3d8817e4Smiod     case wordrel:
194*3d8817e4Smiod     case wordreldef:
195*3d8817e4Smiod       return 3;
196*3d8817e4Smiod     case immediate:
197*3d8817e4Smiod     case absolute:
198*3d8817e4Smiod     case longdisp:
199*3d8817e4Smiod     case longdispdef:
200*3d8817e4Smiod     case longrel:
201*3d8817e4Smiod     case longreldef:
202*3d8817e4Smiod       return 5;
203*3d8817e4Smiod     case indexed:
204*3d8817e4Smiod       return 1 + vax_operandlength (modep + 1);
205*3d8817e4Smiod     }
206*3d8817e4Smiod   /* NOTREACHED */
207*3d8817e4Smiod   abort ();
208*3d8817e4Smiod }
209*3d8817e4Smiod 
210*3d8817e4Smiod static bfd_signed_vma
vax_offset(unsigned char * modep)211*3d8817e4Smiod vax_offset (unsigned char *modep)
212*3d8817e4Smiod {
213*3d8817e4Smiod   operandenum mode = vax_operandmode (modep);
214*3d8817e4Smiod 
215*3d8817e4Smiod   ++modep;				/* skip over the mode */
216*3d8817e4Smiod   switch (mode)
217*3d8817e4Smiod     {
218*3d8817e4Smiod     default:
219*3d8817e4Smiod       fprintf (stderr, "[reladdr] not relative address\n");
220*3d8817e4Smiod       return 0;
221*3d8817e4Smiod     case byterel:
222*3d8817e4Smiod       return 1 + bfd_get_signed_8 (core_bfd, modep);
223*3d8817e4Smiod     case wordrel:
224*3d8817e4Smiod       return 2 + bfd_get_signed_16 (core_bfd, modep);
225*3d8817e4Smiod     case longrel:
226*3d8817e4Smiod       return 4 + bfd_get_signed_32 (core_bfd, modep);
227*3d8817e4Smiod     }
228*3d8817e4Smiod }
229*3d8817e4Smiod 
230*3d8817e4Smiod 
231*3d8817e4Smiod void
vax_find_call(Sym * parent,bfd_vma p_lowpc,bfd_vma p_highpc)232*3d8817e4Smiod vax_find_call (Sym *parent, bfd_vma p_lowpc, bfd_vma p_highpc)
233*3d8817e4Smiod {
234*3d8817e4Smiod   unsigned char *instructp;
235*3d8817e4Smiod   long length;
236*3d8817e4Smiod   Sym *child;
237*3d8817e4Smiod   operandenum mode;
238*3d8817e4Smiod   operandenum firstmode;
239*3d8817e4Smiod   bfd_vma pc, destpc;
240*3d8817e4Smiod   static bfd_boolean inited = FALSE;
241*3d8817e4Smiod 
242*3d8817e4Smiod   if (!inited)
243*3d8817e4Smiod     {
244*3d8817e4Smiod       inited = TRUE;
245*3d8817e4Smiod       sym_init (&indirectchild);
246*3d8817e4Smiod       indirectchild.cg.prop.fract = 1.0;
247*3d8817e4Smiod       indirectchild.cg.cyc.head = &indirectchild;
248*3d8817e4Smiod     }
249*3d8817e4Smiod 
250*3d8817e4Smiod   if (core_text_space == 0)
251*3d8817e4Smiod     {
252*3d8817e4Smiod       return;
253*3d8817e4Smiod     }
254*3d8817e4Smiod   if (p_lowpc < s_lowpc)
255*3d8817e4Smiod     {
256*3d8817e4Smiod       p_lowpc = s_lowpc;
257*3d8817e4Smiod     }
258*3d8817e4Smiod   if (p_highpc > s_highpc)
259*3d8817e4Smiod     {
260*3d8817e4Smiod       p_highpc = s_highpc;
261*3d8817e4Smiod     }
262*3d8817e4Smiod   DBG (CALLDEBUG, printf ("[findcall] %s: 0x%lx to 0x%lx\n",
263*3d8817e4Smiod 			  parent->name, (unsigned long) p_lowpc,
264*3d8817e4Smiod 			  (unsigned long) p_highpc));
265*3d8817e4Smiod   for (pc = p_lowpc; pc < p_highpc; pc += length)
266*3d8817e4Smiod     {
267*3d8817e4Smiod       length = 1;
268*3d8817e4Smiod       instructp = ((unsigned char *) core_text_space
269*3d8817e4Smiod 		   + pc - core_text_sect->vma);
270*3d8817e4Smiod       if ((*instructp & 0xff) == CALLS)
271*3d8817e4Smiod 	{
272*3d8817e4Smiod 	  /*
273*3d8817e4Smiod 	   *    maybe a calls, better check it out.
274*3d8817e4Smiod 	   *      skip the count of the number of arguments.
275*3d8817e4Smiod 	   */
276*3d8817e4Smiod 	  DBG (CALLDEBUG,
277*3d8817e4Smiod 	       printf ("[findcall]\t0x%lx:calls", (unsigned long) pc));
278*3d8817e4Smiod 	  firstmode = vax_operandmode (instructp + length);
279*3d8817e4Smiod 	  switch (firstmode)
280*3d8817e4Smiod 	    {
281*3d8817e4Smiod 	    case literal:
282*3d8817e4Smiod 	    case immediate:
283*3d8817e4Smiod 	      break;
284*3d8817e4Smiod 	    default:
285*3d8817e4Smiod 	      goto botched;
286*3d8817e4Smiod 	    }
287*3d8817e4Smiod 	  length += vax_operandlength (instructp + length);
288*3d8817e4Smiod 	  mode = vax_operandmode (instructp + length);
289*3d8817e4Smiod 	  DBG (CALLDEBUG,
290*3d8817e4Smiod 	       printf ("\tfirst operand is %s", vax_operandname (firstmode));
291*3d8817e4Smiod 	       printf ("\tsecond operand is %s\n", vax_operandname (mode)));
292*3d8817e4Smiod 	  switch (mode)
293*3d8817e4Smiod 	    {
294*3d8817e4Smiod 	    case regdef:
295*3d8817e4Smiod 	    case bytedispdef:
296*3d8817e4Smiod 	    case worddispdef:
297*3d8817e4Smiod 	    case longdispdef:
298*3d8817e4Smiod 	    case bytereldef:
299*3d8817e4Smiod 	    case wordreldef:
300*3d8817e4Smiod 	    case longreldef:
301*3d8817e4Smiod 	      /*
302*3d8817e4Smiod 	       *    indirect call: call through pointer
303*3d8817e4Smiod 	       *      either  *d(r)   as a parameter or local
304*3d8817e4Smiod 	       *              (r)     as a return value
305*3d8817e4Smiod 	       *              *f      as a global pointer
306*3d8817e4Smiod 	       *      [are there others that we miss?,
307*3d8817e4Smiod 	       *       e.g. arrays of pointers to functions???]
308*3d8817e4Smiod 	       */
309*3d8817e4Smiod 	      arc_add (parent, &indirectchild, (unsigned long) 0);
310*3d8817e4Smiod 	      length += vax_operandlength (instructp + length);
311*3d8817e4Smiod 	      continue;
312*3d8817e4Smiod 	    case byterel:
313*3d8817e4Smiod 	    case wordrel:
314*3d8817e4Smiod 	    case longrel:
315*3d8817e4Smiod 	      /*
316*3d8817e4Smiod 	       *    regular pc relative addressing
317*3d8817e4Smiod 	       *      check that this is the address of
318*3d8817e4Smiod 	       *      a function.
319*3d8817e4Smiod 	       */
320*3d8817e4Smiod 	      destpc = pc + vax_offset (instructp + length);
321*3d8817e4Smiod 	      if (destpc >= s_lowpc && destpc <= s_highpc)
322*3d8817e4Smiod 		{
323*3d8817e4Smiod 		  child = sym_lookup (&symtab, destpc);
324*3d8817e4Smiod 		  DBG (CALLDEBUG,
325*3d8817e4Smiod 		       printf ("[findcall]\tdestpc 0x%lx",
326*3d8817e4Smiod 			       (unsigned long) destpc);
327*3d8817e4Smiod 		       printf (" child->name %s", child->name);
328*3d8817e4Smiod 		       printf (" child->addr 0x%lx\n",
329*3d8817e4Smiod 			       (unsigned long) child->addr);
330*3d8817e4Smiod 		    );
331*3d8817e4Smiod 		  if (child->addr == destpc)
332*3d8817e4Smiod 		    {
333*3d8817e4Smiod 		      /*
334*3d8817e4Smiod 		       *    a hit
335*3d8817e4Smiod 		       */
336*3d8817e4Smiod 		      arc_add (parent, child, (unsigned long) 0);
337*3d8817e4Smiod 		      length += vax_operandlength (instructp + length);
338*3d8817e4Smiod 		      continue;
339*3d8817e4Smiod 		    }
340*3d8817e4Smiod 		  goto botched;
341*3d8817e4Smiod 		}
342*3d8817e4Smiod 	      /*
343*3d8817e4Smiod 	       *    else:
344*3d8817e4Smiod 	       *      it looked like a calls,
345*3d8817e4Smiod 	       *      but it wasn't to anywhere.
346*3d8817e4Smiod 	       */
347*3d8817e4Smiod 	      goto botched;
348*3d8817e4Smiod 	    default:
349*3d8817e4Smiod 	    botched:
350*3d8817e4Smiod 	      /*
351*3d8817e4Smiod 	       *    something funny going on.
352*3d8817e4Smiod 	       */
353*3d8817e4Smiod 	      DBG (CALLDEBUG, printf ("[findcall]\tbut it's a botch\n"));
354*3d8817e4Smiod 	      length = 1;
355*3d8817e4Smiod 	      continue;
356*3d8817e4Smiod 	    }
357*3d8817e4Smiod 	}
358*3d8817e4Smiod     }
359*3d8817e4Smiod }
360