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