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