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