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 DBG (CALLDEBUG, printf ("[findcall] %s: 0x%lx to 0x%lx\n", 239 parent->name, (unsigned long) p_lowpc, 240 (unsigned long) p_highpc)); 241 for (pc = p_lowpc; pc < p_highpc; pc += length) 242 { 243 length = 1; 244 instructp = ((unsigned char *) core_text_space 245 + pc - core_text_sect->vma); 246 if ((*instructp & 0xff) == CALLF) 247 { 248 /* 249 * maybe a callf, better check it out. 250 * skip the count of the number of arguments. 251 */ 252 DBG (CALLDEBUG, printf ("[findcall]\t0x%lx:callf", 253 (unsigned long) pc)); 254 firstmode = tahoe_operandmode (instructp + length); 255 switch (firstmode) 256 { 257 case literal: 258 case immediate: 259 break; 260 default: 261 goto botched; 262 } 263 length += tahoe_operandlength (instructp + length); 264 mode = tahoe_operandmode (instructp + length); 265 DBG (CALLDEBUG, 266 printf ("\tfirst operand is %s", tahoe_operandname (firstmode)); 267 printf ("\tsecond operand is %s\n", tahoe_operandname (mode)); 268 ); 269 switch (mode) 270 { 271 case regdef: 272 case bytedispdef: 273 case worddispdef: 274 case longdispdef: 275 case bytereldef: 276 case wordreldef: 277 case longreldef: 278 /* 279 * indirect call: call through pointer 280 * either *d(r) as a parameter or local 281 * (r) as a return value 282 * *f as a global pointer 283 * [are there others that we miss?, 284 * e.g. arrays of pointers to functions???] 285 */ 286 arc_add (parent, &indirectchild, (unsigned long) 0); 287 length += tahoe_operandlength (instructp + length); 288 continue; 289 case byterel: 290 case wordrel: 291 case longrel: 292 /* 293 * regular pc relative addressing 294 * check that this is the address of 295 * a function. 296 */ 297 destpc = pc + tahoe_offset (instructp + length); 298 if (hist_check_address (destpc)) 299 { 300 child = sym_lookup (&symtab, destpc); 301 if (child) 302 { 303 DBG (CALLDEBUG, 304 printf ("[findcall]\tdestpc 0x%lx", 305 (unsigned long) destpc); 306 printf (" child->name %s", child->name); 307 printf (" child->addr 0x%lx\n", 308 (unsigned long) child->addr); 309 ); 310 if (child->addr == destpc) 311 { 312 /* 313 * a hit 314 */ 315 arc_add (parent, child, (unsigned long) 0); 316 length += tahoe_operandlength (instructp + length); 317 continue; 318 } 319 } 320 goto botched; 321 } 322 /* 323 * else: 324 * it looked like a callf, 325 * but it wasn't to anywhere. 326 */ 327 goto botched; 328 default: 329 botched: 330 /* 331 * something funny going on. 332 */ 333 DBG (CALLDEBUG, printf ("[findcall]\tbut it's a botch\n")); 334 length = 1; 335 continue; 336 } 337 } 338 } 339 } 340