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 /* *INDENT-OFF* */ 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 60 struct modebyte 61 { 62 unsigned int regfield:4; 63 unsigned int modefield:4; 64 }; 65 */ 66 /* *INDENT-ON* */ 67 68 /* 69 * A symbol to be the child of indirect calls: 70 */ 71 static Sym indirectchild; 72 73 static operandenum vax_operandmode (unsigned char *); 74 static char *vax_operandname (operandenum); 75 static long vax_operandlength (unsigned char *); 76 static bfd_signed_vma vax_offset (unsigned char *); 77 void vax_find_call (Sym *, bfd_vma, bfd_vma); 78 79 static operandenum 80 vax_operandmode (unsigned char *modep) 81 { 82 int usesreg = *modep & 0xf; 83 84 switch ((*modep >> 4) & 0xf) 85 { 86 case 0: 87 case 1: 88 case 2: 89 case 3: 90 return literal; 91 case 4: 92 return indexed; 93 case 5: 94 return reg; 95 case 6: 96 return regdef; 97 case 7: 98 return autodec; 99 case 8: 100 return usesreg != PC ? autoinc : immediate; 101 case 9: 102 return usesreg != PC ? autoincdef : absolute; 103 case 10: 104 return usesreg != PC ? bytedisp : byterel; 105 case 11: 106 return usesreg != PC ? bytedispdef : bytereldef; 107 case 12: 108 return usesreg != PC ? worddisp : wordrel; 109 case 13: 110 return usesreg != PC ? worddispdef : wordreldef; 111 case 14: 112 return usesreg != PC ? longdisp : longrel; 113 case 15: 114 return usesreg != PC ? longdispdef : longreldef; 115 } 116 /* NOTREACHED */ 117 abort (); 118 } 119 120 static char * 121 vax_operandname (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 (unsigned char *modep) 175 { 176 177 switch (vax_operandmode (modep)) 178 { 179 case literal: 180 case reg: 181 case regdef: 182 case autodec: 183 case autoinc: 184 case autoincdef: 185 return 1; 186 case bytedisp: 187 case bytedispdef: 188 case byterel: 189 case bytereldef: 190 return 2; 191 case worddisp: 192 case worddispdef: 193 case wordrel: 194 case wordreldef: 195 return 3; 196 case immediate: 197 case absolute: 198 case longdisp: 199 case longdispdef: 200 case longrel: 201 case longreldef: 202 return 5; 203 case indexed: 204 return 1 + vax_operandlength (modep + 1); 205 } 206 /* NOTREACHED */ 207 abort (); 208 } 209 210 static bfd_signed_vma 211 vax_offset (unsigned char *modep) 212 { 213 operandenum mode = vax_operandmode (modep); 214 215 ++modep; /* skip over the mode */ 216 switch (mode) 217 { 218 default: 219 fprintf (stderr, "[reladdr] not relative address\n"); 220 return 0; 221 case byterel: 222 return 1 + bfd_get_signed_8 (core_bfd, modep); 223 case wordrel: 224 return 2 + bfd_get_signed_16 (core_bfd, modep); 225 case longrel: 226 return 4 + bfd_get_signed_32 (core_bfd, modep); 227 } 228 } 229 230 231 void 232 vax_find_call (Sym *parent, bfd_vma p_lowpc, bfd_vma p_highpc) 233 { 234 unsigned char *instructp; 235 long length; 236 Sym *child; 237 operandenum mode; 238 operandenum firstmode; 239 bfd_vma pc, destpc; 240 static bool inited = false; 241 242 if (!inited) 243 { 244 inited = true; 245 sym_init (&indirectchild); 246 indirectchild.cg.prop.fract = 1.0; 247 indirectchild.cg.cyc.head = &indirectchild; 248 } 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 unsigned char *operand; 256 257 length = 1; 258 instructp = ((unsigned char *) core_text_space 259 + pc - core_text_sect->vma); 260 if ((*instructp & 0xff) == CALLS) 261 { 262 /* 263 * maybe a calls, better check it out. 264 * skip the count of the number of arguments. 265 */ 266 DBG (CALLDEBUG, 267 printf ("[findcall]\t0x%lx:calls", (unsigned long) pc)); 268 if (pc - core_text_sect->vma + length >= core_text_sect->size) 269 goto botched; 270 operand = instructp + length; 271 firstmode = vax_operandmode (operand); 272 switch (firstmode) 273 { 274 case literal: 275 case immediate: 276 break; 277 default: 278 goto botched; 279 } 280 length += vax_operandlength (operand); 281 if (pc - core_text_sect->vma + length >= core_text_sect->size) 282 goto botched; 283 operand = instructp + length; 284 mode = vax_operandmode (operand); 285 DBG (CALLDEBUG, 286 printf ("\tfirst operand is %s", vax_operandname (firstmode)); 287 printf ("\tsecond operand is %s\n", vax_operandname (mode))); 288 switch (mode) 289 { 290 case regdef: 291 case bytedispdef: 292 case worddispdef: 293 case longdispdef: 294 case bytereldef: 295 case wordreldef: 296 case longreldef: 297 /* 298 * indirect call: call through pointer 299 * either *d(r) as a parameter or local 300 * (r) as a return value 301 * *f as a global pointer 302 * [are there others that we miss?, 303 * e.g. arrays of pointers to functions???] 304 */ 305 length += vax_operandlength (operand); 306 if (pc - core_text_sect->vma + length > core_text_sect->size) 307 goto botched; 308 arc_add (parent, &indirectchild, (unsigned long) 0); 309 continue; 310 case byterel: 311 case wordrel: 312 case longrel: 313 /* 314 * regular pc relative addressing 315 * check that this is the address of 316 * a function. 317 */ 318 length += vax_operandlength (operand); 319 if (pc - core_text_sect->vma + length > core_text_sect->size) 320 goto botched; 321 destpc = pc + vax_offset (operand); 322 if (hist_check_address (destpc)) 323 { 324 child = sym_lookup (&symtab, destpc); 325 if (child) 326 { 327 DBG (CALLDEBUG, 328 printf ("[findcall]\tdestpc 0x%lx", 329 (unsigned long) destpc); 330 printf (" child->name %s", child->name); 331 printf (" child->addr 0x%lx\n", 332 (unsigned long) child->addr); 333 ); 334 if (child->addr == destpc) 335 { 336 /* 337 * a hit 338 */ 339 arc_add (parent, child, (unsigned long) 0); 340 continue; 341 } 342 } 343 goto botched; 344 } 345 /* 346 * else: 347 * it looked like a calls, 348 * but it wasn't to anywhere. 349 */ 350 goto botched; 351 default: 352 botched: 353 /* 354 * something funny going on. 355 */ 356 DBG (CALLDEBUG, printf ("[findcall]\tbut it's a botch\n")); 357 length = 1; 358 continue; 359 } 360 } 361 } 362 } 363