HPCToolkit
x86-process-ranges.cpp
Go to the documentation of this file.
1 // -*-Mode: C++;-*-
2 
3 // * BeginRiceCopyright *****************************************************
4 //
5 // $HeadURL$
6 // $Id$
7 //
8 // --------------------------------------------------------------------------
9 // Part of HPCToolkit (hpctoolkit.org)
10 //
11 // Information about sources of support for research and development of
12 // HPCToolkit is at 'hpctoolkit.org' and in 'README.Acknowledgments'.
13 // --------------------------------------------------------------------------
14 //
15 // Copyright ((c)) 2002-2019, Rice University
16 // All rights reserved.
17 //
18 // Redistribution and use in source and binary forms, with or without
19 // modification, are permitted provided that the following conditions are
20 // met:
21 //
22 // * Redistributions of source code must retain the above copyright
23 // notice, this list of conditions and the following disclaimer.
24 //
25 // * Redistributions in binary form must reproduce the above copyright
26 // notice, this list of conditions and the following disclaimer in the
27 // documentation and/or other materials provided with the distribution.
28 //
29 // * Neither the name of Rice University (RICE) nor the names of its
30 // contributors may be used to endorse or promote products derived from
31 // this software without specific prior written permission.
32 //
33 // This software is provided by RICE and contributors "as is" and any
34 // express or implied warranties, including, but not limited to, the
35 // implied warranties of merchantability and fitness for a particular
36 // purpose are disclaimed. In no event shall RICE or contributors be
37 // liable for any direct, indirect, incidental, special, exemplary, or
38 // consequential damages (including, but not limited to, procurement of
39 // substitute goods or services; loss of use, data, or profits; or
40 // business interruption) however caused and on any theory of liability,
41 // whether in contract, strict liability, or tort (including negligence
42 // or otherwise) arising in any way out of the use of this software, even
43 // if advised of the possibility of such damage.
44 //
45 // ******************************************************* EndRiceCopyright *
46 
47 /******************************************************************************
48  * system include files
49  *****************************************************************************/
50 
51 
52 #include <stdio.h>
53 #include <assert.h>
54 #include <string>
55 
56 #include <include/hpctoolkit-config.h>
57 
58 /******************************************************************************
59  * XED include files, and conditionally the amd extended ops (amd-xop)
60  *****************************************************************************/
61 
62 extern "C" {
63 #include <include/hpctoolkit-config.h>
64 #include <xed-interface.h>
65 #if defined(ENABLE_XOP) && defined (HOST_CPU_x86_64)
66 #include "amd-xop.h"
67 #endif // ENABLE_XOP
68 
69 // debug callable routines -- only callable after xed_tables_init has been called
70 //
71 
72  static xed_state_t dbg_xed_machine_state =
73 #if defined (HOST_CPU_x86_64)
74  { XED_MACHINE_MODE_LONG_64,
75  XED_ADDRESS_WIDTH_64b };
76 #else
77  { XED_MACHINE_MODE_LONG_COMPAT_32,
78  XED_ADDRESS_WIDTH_32b };
79 #endif
80 
81 xed_iclass_enum_t
82 xed_iclass(char* ins)
83 {
84  xed_decoded_inst_t xedd;
85  xed_decoded_inst_t *xptr = &xedd;
86 
87  xed_decoded_inst_zero_set_mode(xptr, &dbg_xed_machine_state);
88 
89  xed_error_enum_t xed_error = xed_decode(xptr, (uint8_t*) ins, 15);
90  if (xed_error != XED_ERROR_NONE) {
91  fprintf(stderr, "!! XED decode failure of insruction @ %p", ins);
92  return XED_ICLASS_INVALID;
93  }
94  return xed_decoded_inst_get_iclass(xptr);
95 }
96 
97 };
98 
99 
100 /******************************************************************************
101  * include files
102  *****************************************************************************/
103 
104 #include "code-ranges.h"
105 #include "function-entries.h"
106 #include "process-ranges.h"
107 
108 #include <include/hpctoolkit-config.h>
110 
111 
112 /******************************************************************************
113  * forward declarations
114  *****************************************************************************/
115 
116 static void process_call(char *ins, long offset, xed_decoded_inst_t *xptr,
117  void *start, void *end);
118 
119 static bool is_push_bp(char* ins);
120 
121 static bool is_sub_immed_sp(char* ins, char** next);
122 static bool is_2step_push_bp(char* ins);
123 static bool contains_bp_save(char* ins);
124 
125 static bool is_push_bp_seq(char* ins);
126 
127 static void process_branch(char *ins, long offset, xed_decoded_inst_t *xptr, char* vstart, char* vend);
128 
129 static void after_unconditional(char *ins, long offset, xed_decoded_inst_t *xptr);
130 
131 static bool invalid_routine_start(unsigned char *ins);
132 
133 static void addsub(char *ins, xed_decoded_inst_t *xptr, xed_iclass_enum_t iclass,
134  long ins_offset);
135 
136 static void process_move(char *ins, xed_decoded_inst_t *xptr, long ins_offset);
137 
138 static void process_push(char *ins, xed_decoded_inst_t *xptr, long ins_offset);
139 
140 static void process_pop(char *ins, xed_decoded_inst_t *xptr, long ins_offset);
141 
142 static void process_enter(char *ins, long ins_offset);
143 
144 static void process_leave(char *ins, long ins_offset);
145 
146 static bool bkwd_jump_into_protected_range(char *ins, long offset,
147  xed_decoded_inst_t *xptr);
148 
149 static bool validate_tail_call_from_jump(char *ins, long offset,
150  xed_decoded_inst_t *xptr);
151 
152 static bool nextins_looks_like_fn_start(char *ins, long offset,
153  xed_decoded_inst_t *xptrin);
154 
155 static bool lea_has_zero_offset(xed_decoded_inst_t *xptr);
156 
157 #define RELOCATE(u, offset) (((char *) (u)) - (offset))
158 
159 
160 /******************************************************************************
161  * local variables
162  *****************************************************************************/
163 
164 static xed_state_t xed_machine_state =
165 #if defined (HOST_CPU_x86_64)
166  { XED_MACHINE_MODE_LONG_64,
167  XED_ADDRESS_WIDTH_64b };
168 #else
169  { XED_MACHINE_MODE_LONG_COMPAT_32,
170  XED_ADDRESS_WIDTH_32b };
171 #endif
172 
173 static char *prologue_start = NULL;
174 static char *set_rbp = NULL;
175 static char *push_rbp = NULL;
176 static char *push_other = NULL;
177 static char *last_bad = NULL;
178 static xed_reg_enum_t push_other_reg;
179 
180 
181 /******************************************************************************
182  * Debugging Macros
183  *****************************************************************************/
184 
185 // Uncomment (e.g. activate) the following definition
186 // to access instruction byte stream.
187 // (Most routines taking the "ins" argument use a relocated address. Maintaining
188 // the rel_offset variable permits access to the actual instruction byte stream)
189 //
190 
191 // #define DBG_INST_STRM
192 
193 // Uncomment (e.g. activate) the following definition
194 // to get diagnostic print out of instruction stream when branch target offset is
195 // 2 bytes.
196 //
197 
198 // #define DBG_BR_TARG_2
199 
200 #ifdef DBG_INST_STRM
201 
202 static size_t rel_offset = 0;
203 
204 # define SAVE_REL_OFFSET(offset) rel_offset = offset
205 # define KILL_REL_OFFSET() rel_offset = 0
206 
207 #else
208 # define SAVE_REL_OFFSET(offset)
209 # define KILL_REL_OFFSET()
210 
211 #endif // DBG_INST_STRM
212 
213 
214 
215 
216 /******************************************************************************
217  * interface operations
218  *****************************************************************************/
219 
220 void
222 {
223  xed_tables_init();
224 }
225 
226 
227 void
228 process_range(const char *name, long offset, void *vstart, void *vend,
229  DiscoverFnTy fn_discovery)
230 {
231  if (fn_discovery == DiscoverFnTy_None) {
232  return;
233  }
234 
235  xed_decoded_inst_t xedd;
236  xed_decoded_inst_t *xptr = &xedd;
237  xed_error_enum_t xed_error;
238 
239  int error_count = 0;
240  char *ins = (char *) vstart;
241  char *end = (char *) vend;
242  vector<void *> fstarts;
243  entries_in_range(ins + offset, end + offset, fstarts);
244 
245  void **fstart = &fstarts[0];
246  char *guidepost = RELOCATE(*fstart, offset);
247 
248  xed_decoded_inst_zero_set_mode(xptr, &xed_machine_state);
249 
250 #ifdef DEBUG
251  xed_iclass_enum_t prev_xiclass = XED_ICLASS_INVALID;
252 #endif
253 
254  while (ins < end) {
255 
256 #ifdef DEBUG
257  // vins = virtual address of instruction as we expect it in the object file
258  // (this is useful because we can set a breakpoint to watch a vins)
259  char *vins = ins + offset;
260 #endif
261 
262  if (ins >= guidepost) {
263  if (ins > guidepost) {
264  //--------------------------------------------------------------
265  // we missed a guidepost; disassembly was not properly aligned.
266  // realign ins to match the guidepost
267  //--------------------------------------------------------------
268 #ifdef DEBUG_GUIDEPOST
269  printf("resetting ins to guidepost %p from %p\n",
270  guidepost + offset, ins + offset);
271 #endif
272  ins = guidepost;
273  }
274  //----------------------------------------------------------------
275  // all is well; our disassembly is properly aligned.
276  // advance to the next guidepost
277  //
278  // NOTE: since the vector of fstarts contains end,
279  // the fstart pointer will never go past the end of the
280  // fstarts array.
281  //----------------------------------------------------------------
282  fstart++; guidepost = RELOCATE(*fstart, offset);
283  }
284 
285  xed_decoded_inst_zero_keep_mode(xptr);
286  xed_error = xed_decode(xptr, (uint8_t*) ins, 15);
287 
288  if (xed_error != XED_ERROR_NONE) {
289 #if defined(ENABLE_XOP) && defined (HOST_CPU_x86_64)
290  amd_decode_t decode_res;
291  adv_amd_decode(&decode_res, (uint8_t*) ins);
292  if (decode_res.success) {
293  if (decode_res.weak) {
294  // keep count of successes that are not robust
295  }
296  ins += decode_res.len;
297  continue;
298  }
299 #endif // ENABLE_XOP && HOST_CPU_x86_64
300  last_bad = ins;
301  error_count++; /* note the error */
302  ins++; /* skip this byte */
303  continue; /* continue onward ... */
304  }
305 
306  xed_iclass_enum_t xiclass = xed_decoded_inst_get_iclass(xptr);
307  switch(xiclass) {
308  case XED_ICLASS_ADD:
309  case XED_ICLASS_SUB:
310  addsub(ins, xptr, xiclass, offset);
311  break;
312  case XED_ICLASS_CALL_FAR:
313  case XED_ICLASS_CALL_NEAR:
314  /* if (fn_discovery == DiscoverFnTy_Aggressive) */
315  process_call(ins, offset, xptr, vstart, vend);
316  break;
317 
318  case XED_ICLASS_JMP:
319  case XED_ICLASS_JMP_FAR:
320  if (xed_decoded_inst_noperands(xptr) == 2) {
321  const xed_inst_t *xi = xed_decoded_inst_inst(xptr);
322  const xed_operand_t *op0 = xed_inst_operand(xi, 0);
323  const xed_operand_t *op1 = xed_inst_operand(xi, 1);
324  //xed_operand_type_enum_t op0_type = xed_operand_type(op0); // unused
325  //xed_operand_type_enum_t op1_type = xed_operand_type(op1); // unused
326  if ((xed_operand_name(op0) == XED_OPERAND_MEM0) &&
327  (xed_operand_name(op1) == XED_OPERAND_REG0) &&
328  x86_isReg_IP(xed_decoded_inst_get_base_reg(xptr, 1))) {
329  // idiom for GOT indexing in PLT
330  // don't consider the instruction afterward a potential function start
331  break;
332  }
333  if ((xed_operand_name(op0) == XED_OPERAND_REG0) &&
334  (xed_operand_name(op1) == XED_OPERAND_REG1) &&
335  x86_isReg_IP(xed_decoded_inst_get_base_reg(xptr, 1))) {
336  // idiom for a switch using a jump table:
337  // don't consider the instruction afterward a potential function start
338  break;
339  }
340  }
341  bkwd_jump_into_protected_range(ins, offset, xptr);
342  if (fn_discovery &&
343  (validate_tail_call_from_jump(ins, offset, xptr) ||
344  nextins_looks_like_fn_start(ins, offset, xptr))) {
345  after_unconditional(ins, offset, xptr);
346  }
347  break;
348  case XED_ICLASS_RET_FAR:
349  case XED_ICLASS_RET_NEAR:
350  if (fn_discovery == DiscoverFnTy_Aggressive) {
351  after_unconditional(ins, offset, xptr);
352  }
353  break;
354 
355  case XED_ICLASS_JB:
356  case XED_ICLASS_JBE:
357  case XED_ICLASS_JL:
358  case XED_ICLASS_JLE:
359  case XED_ICLASS_JNB:
360  case XED_ICLASS_JNBE:
361  case XED_ICLASS_JNL:
362  case XED_ICLASS_JNLE:
363  case XED_ICLASS_JNO:
364  case XED_ICLASS_JNP:
365  case XED_ICLASS_JNS:
366  case XED_ICLASS_JNZ:
367  case XED_ICLASS_JO:
368  case XED_ICLASS_JP:
369  case XED_ICLASS_JRCXZ:
370  case XED_ICLASS_JS:
371  case XED_ICLASS_JZ:
372  if (fn_discovery == DiscoverFnTy_Aggressive) {
373  process_branch(ins, offset , xptr, (char*) vstart, (char*) vend);
374  }
375  break;
376 
377  case XED_ICLASS_LOOP:
378  case XED_ICLASS_LOOPE:
379  case XED_ICLASS_LOOPNE:
380  if (fn_discovery == DiscoverFnTy_Aggressive) {
381  process_branch(ins, offset , xptr, (char*) vstart, (char*) vend);
382  }
383  break;
384 
385  case XED_ICLASS_PUSH:
386  case XED_ICLASS_PUSHFQ:
387  case XED_ICLASS_PUSHFD:
388  case XED_ICLASS_PUSHF:
389  process_push(ins, xptr, offset);
390  break;
391 
392  case XED_ICLASS_POP:
393  case XED_ICLASS_POPF:
394  case XED_ICLASS_POPFD:
395  case XED_ICLASS_POPFQ:
396  process_pop(ins, xptr, offset);
397  break;
398 
399  case XED_ICLASS_ENTER:
400  process_enter(ins, offset);
401  break;
402 
403  case XED_ICLASS_MOV:
404  process_move(ins, xptr, offset);
405  break;
406 
407  case XED_ICLASS_LEAVE:
408  process_leave(ins, offset);
409  break;
410 
411  default:
412  break;
413  }
414 
415 #ifdef DEBUG
416  prev_xiclass = xiclass;
417 #endif
418 
419  ins += xed_decoded_inst_get_length(xptr);
420  }
421 }
422 
423 
424 
425 /******************************************************************************
426  * private operations
427  *****************************************************************************/
428 
429 static int
431 {
432  return (c == 0x66) || (c == 0x90);
433 }
434 
435 static bool
436 skip_padding(unsigned char **ins)
437 {
438  bool notdone = true;
439  xed_decoded_inst_t xedd_tmp;
440  xed_decoded_inst_t *xptr = &xedd_tmp;
441  xed_error_enum_t xed_error;
442 
443  xed_decoded_inst_zero_set_mode(xptr, &xed_machine_state);
444 
445  while(notdone) {
446  xed_decoded_inst_zero_keep_mode(xptr);
447  xed_error = xed_decode(xptr, (uint8_t*) *ins, 15);
448 
449  if (xed_error != XED_ERROR_NONE) return false;
450 
451  xed_iclass_enum_t xiclass = xed_decoded_inst_get_iclass(xptr);
452  switch(xiclass) {
453  case XED_ICLASS_LEA:
454  if (!lea_has_zero_offset(xptr)) return false;
455  // gcc seems to use 0 offset lea as padding between functions
456  // treat it as a nop
457  case XED_ICLASS_NOP: case XED_ICLASS_NOP2: case XED_ICLASS_NOP3:
458  case XED_ICLASS_NOP4: case XED_ICLASS_NOP5: case XED_ICLASS_NOP6:
459  case XED_ICLASS_NOP7: case XED_ICLASS_NOP8: case XED_ICLASS_NOP9:
460  // nop: move to the next instruction
461  *ins = *ins + xed_decoded_inst_get_length(xptr);
462  break;
463  default:
464  // a valid instruction but not a nop; we are done skipping
465  notdone = false;
466  break;
467  }
468  }
469  return true;
470 }
471 
472 
473 //----------------------------------------------------------------------------
474 // code that is unreachable after a return or an unconditional jump is a
475 // potential function entry point. try to screen out the cases that don't
476 // make sense. infer function entry points for the rest.
477 //----------------------------------------------------------------------------
478 static void
479 after_unconditional(char *ins, long offset, xed_decoded_inst_t *xptr)
480 {
481  ins += xed_decoded_inst_get_length(xptr);
482 
483  if (inside_protected_range(ins + offset)) return;
484 
485  unsigned char *new_func_addr = (unsigned char *) ins;
486  if (skip_padding(&new_func_addr) == false) {
487  // false = not a valid instruction;
488  // this can't be the start of a new function
489  return;
490  }
491  if (contains_function_entry(new_func_addr + offset) == false) {
492  if (!invalid_routine_start(new_func_addr)) {
493 #if 0
494  fn_start = WEAK;
495  if () { // push bp OR
496  // save something at offset rel to sp OR
497  // arith on sp
498  // ==> strong evidence f fn start.
499  }
500 #endif
501  if (is_push_bp_seq(ins)) {
502  // consider this to be strong evidence. this case is necessary
503  // for recognizing OpenMP tasks and parallel regions outlined
504  // by the icc compiler. since they aren't called directly, there
505  // wasn't enough evidence to cause them to rate reporting as
506  // stripped functions with the case below.
507  // johnmc 10/20/13
508  add_function_entry(new_func_addr + offset, NULL, true /* isvisible */, 0);
509  } else {
510  add_stripped_function_entry(new_func_addr + offset);
511  }
512  }
513  }
514 }
515 
516 static void *
517 get_branch_target(char *ins, xed_decoded_inst_t *xptr,
518  xed_operand_values_t *vals)
519 {
520  int offset = xed_operand_values_get_branch_displacement_int32(vals);
521  char* insn_end = ins + xed_decoded_inst_get_length(xptr);
522  void* target = (void*)(insn_end + offset);
523 
524 #if defined(DBG_BR_TARG_2) && defined(DBG_INST_STRM)
525  int bytes = xed_operand_values_get_branch_displacement_length(vals);
526 
527  if (bytes == 2) {
528  fprintf(stderr, "Reached case 2 @ location: %p, offset32 = %x\n", ins, offset);
529  fprintf(stderr, "byte seq @ %p = \n ", ins);
530  for (struct {unsigned i; unsigned char* p;} l = {0, (unsigned char*)(ins - rel_offset)};
531  l.i < xed_decoded_inst_get_length(xptr);
532  l.i++, l.p++) {
533  fprintf(stderr, "%x ", *l.p);
534  }
535  fprintf(stderr, "\n");
536  }
537 #endif // DBG_BR_TARG_2 && DBG_INST_STRM
538 
539  return target;
540 }
541 
542 
543 //
544 // special purpose check for some flavor of 'push bp' instruction
545 //
546 static bool
547 is_push_bp(char* ins)
548 {
549  xed_decoded_inst_t xedd_tmp;
550  xed_decoded_inst_t *xptr = &xedd_tmp;
551  xed_error_enum_t xed_error;
552 
553 
554  xed_decoded_inst_zero_set_mode(xptr, &xed_machine_state);
555  xed_decoded_inst_zero_keep_mode(xptr);
556 
557  xed_error = xed_decode(xptr, (uint8_t*) ins, 15);
558 
559  if (xed_error != XED_ERROR_NONE) return false;
560 
561  xed_iclass_enum_t xiclass = xed_decoded_inst_get_iclass(xptr);
562 
563  switch(xiclass) {
564  case XED_ICLASS_PUSH:
565  case XED_ICLASS_PUSHFQ:
566  case XED_ICLASS_PUSHFD:
567  case XED_ICLASS_PUSHF:
568  {
569  //
570  // return true if push argument == some kind of bp
571  //
572  const xed_inst_t* xi = xed_decoded_inst_inst(xptr);
573  const xed_operand_t* op0 = xed_inst_operand(xi, 0);
574  xed_operand_enum_t op0_name = xed_operand_name(op0);
575 
576  if (op0_name == XED_OPERAND_REG0) {
577  xed_reg_enum_t regname = xed_decoded_inst_get_reg(xptr, op0_name);
578  return x86_isReg_BP(regname);
579  }
580  else {
581  return false;
582  }
583  }
584  break;
585  default:
586  return false;
587  break;
588  }
589 }
590 
591 //
592 // check to see if there is a 'mov bp, OFFSET[sp]' within the following WINDOW instructions
593 //
594 
595 static const size_t WINDOW = 16; // 16 instruction window
596 
597 // true if save 'bp' on stack within next window instructions
598 //
599 static bool
600 contains_bp_save_window(char* ins, size_t window)
601 {
602  for (size_t n = 0; n < window; n++) {
603  xed_decoded_inst_t xedd_tmp;
604  xed_decoded_inst_t *xptr = &xedd_tmp;
605  xed_error_enum_t xed_error;
606 
607  xed_decoded_inst_zero_set_mode(xptr, &xed_machine_state);
608  xed_decoded_inst_zero_keep_mode(xptr);
609 
610  xed_error = xed_decode(xptr, (uint8_t*) ins, 15);
611 
612  if (xed_error != XED_ERROR_NONE) return false;
613 
614  xed_iclass_enum_t xiclass = xed_decoded_inst_get_iclass(xptr);
615 
616  if (xiclass == XED_ICLASS_MOV) {
617  const xed_inst_t* xi = xed_decoded_inst_inst(xptr);
618  const xed_operand_t* op0 = xed_inst_operand(xi, 0);
619  xed_operand_enum_t op0_name = xed_operand_name(op0);
620  const xed_operand_t* op1 = xed_inst_operand(xi,1);
621  xed_operand_enum_t op1_name = xed_operand_name(op1);
622 
623  if ((op0_name == XED_OPERAND_MEM0) && (op1_name == XED_OPERAND_REG0)) {
624 
625  xed_reg_enum_t basereg = xed_decoded_inst_get_base_reg(xptr, 0);
626  if (x86_isReg_SP(basereg)) {
627  xed_reg_enum_t reg1 = xed_decoded_inst_get_reg(xptr, op1_name);
628  if (x86_isReg_BP(reg1)) return true;
629  }
630  }
631  }
632  ins += xed_decoded_inst_get_length(xptr);
633  }
634  return false;
635 }
636 
637 // Utility routine to check for a specific window for bp save
638 //
639 static bool
641 {
642  return contains_bp_save_window(ins, WINDOW);
643 }
644 
645 //
646 // check for subtract from sp:
647 // SIDE EFFECT: return the address of the next instruction
648 //
649 static bool
650 is_sub_immed_sp(char* ins, char** next)
651 {
652  xed_decoded_inst_t xedd_tmp;
653  xed_decoded_inst_t *xptr = &xedd_tmp;
654  xed_error_enum_t xed_error;
655 
656 
657  xed_decoded_inst_zero_set_mode(xptr, &xed_machine_state);
658  xed_decoded_inst_zero_keep_mode(xptr);
659 
660  xed_error = xed_decode(xptr, (uint8_t*) ins, 15);
661 
662  if (xed_error != XED_ERROR_NONE) return false;
663 
664  xed_iclass_enum_t xiclass = xed_decoded_inst_get_iclass(xptr);
665 
666  if (xiclass != XED_ICLASS_SUB) return false;
667  //
668  // return true if const amt subtracted f sp
669  //
670  const xed_inst_t* xi = xed_decoded_inst_inst(xptr);
671  const xed_operand_t* op0 = xed_inst_operand(xi, 0);
672  xed_operand_enum_t op0_name = xed_operand_name(op0);
673 
674  if (op0_name != XED_OPERAND_REG0) return false;
675 
676  xed_reg_enum_t regname = xed_decoded_inst_get_reg(xptr, op0_name);
677  const xed_operand_t* op1 = xed_inst_operand(xi,1);
678  *next = ins + xed_decoded_inst_get_length(xptr);
679 
680  return (x86_isReg_SP(regname) && (xed_operand_name(op1) == XED_OPERAND_IMM0));
681 }
682 
683 //
684 // 2-step-push-bp ==
685 // sub SOMETHING, %sp
686 // .... MAYBE SOME OTHER INSTRUCTIONS ...
687 // mov bp, SOME_OFFSET[%sp]
688 //
689 
690 static bool
692 {
693  char* next = NULL;
694  if (is_sub_immed_sp(ins, &next)) {
695  return true;
696  }
697  return false;
698 }
699 
700 static bool
702 {
703  char* next = NULL;
704  if (is_sub_immed_sp(ins, &next)) {
705  return contains_bp_save(next);
706  }
707  return false;
708 }
709 
710 static bool
711 is_push_bp_seq(char* ins)
712 {
713  return is_push_bp(ins) ||
714  contains_bp_save_window(ins, 1) ||
715  is_sub_immed_prologue(ins) ||
716  is_2step_push_bp(ins);
717 }
718 
719 static void
720 process_call(char *ins, long offset, xed_decoded_inst_t *xptr,
721  void *start, void *end)
722 {
723  const xed_inst_t *xi = xed_decoded_inst_inst(xptr);
724  const xed_operand_t *op0 = xed_inst_operand(xi, 0);
725  xed_operand_enum_t op0_name = xed_operand_name(op0);
726  xed_operand_type_enum_t op0_type = xed_operand_type(op0);
727 
728  if (op0_name == XED_OPERAND_RELBR &&
729  op0_type == XED_OPERAND_TYPE_IMM_CONST) {
730  xed_operand_values_t *vals = xed_decoded_inst_operands(xptr);
731 
732  if (xed_operand_values_has_branch_displacement(vals)) {
733  int call_inst_len = xed_decoded_inst_get_length(xptr);
734  void *next_inst_vaddr = ((char *)ins) + offset + call_inst_len;
735 
736  SAVE_REL_OFFSET(offset);
737 
738  void* vaddr = get_branch_target(ins + offset, xptr, vals);
739 
740  KILL_REL_OFFSET();
741 
742  if (vaddr != next_inst_vaddr && consider_possible_fn_address(vaddr)) {
743  //
744  // if called address is a 'push bp' sequence of instructions,
745  // [ ie, either a single 'push bp', or a 'sub NNN, sp', followed by
746  // a 'mov bp, MMM[sp] ]
747  // then the target address of
748  // this call is considered a legitimate function start,
749  // and the function entry has the 'isvisible' fields set to true
750  //
751  if ( is_push_bp_seq((char*)vaddr - offset) ) {
752  add_function_entry(vaddr, NULL, true /* isvisible */, 1 /* call count */);
753  }
754  //
755  // otherwise, called address is weak function start, subject to later filtering
756  //
757  else {
758  add_stripped_function_entry(vaddr, 1 /* call count */);
759  }
760  }
761  }
762 
763  }
764  // some function calls don't return; we have seen the last instruction
765  // in a routine be a call to a function that doesn't return
766  // (e.g. _gfortrani_internal_unpack_c8 in libgfortran ends in a call to abort)
767  // look to see if a function signature appears next. mark a routine start
768  // if appropriate
769  nextins_looks_like_fn_start(ins, offset, xptr);
770 }
771 
772 
773 static bool
774 bkwd_jump_into_protected_range(char *ins, long offset, xed_decoded_inst_t *xptr)
775 {
776  char *relocated_ins = ins + offset;
777  const xed_inst_t *xi = xed_decoded_inst_inst(xptr);
778  const xed_operand_t *op0 = xed_inst_operand(xi, 0);
779  xed_operand_enum_t op0_name = xed_operand_name(op0);
780  xed_operand_type_enum_t op0_type = xed_operand_type(op0);
781 
782  if (op0_name == XED_OPERAND_RELBR &&
783  op0_type == XED_OPERAND_TYPE_IMM_CONST) {
784  xed_operand_values_t *vals = xed_decoded_inst_operands(xptr);
785 
786  if (xed_operand_values_has_branch_displacement(vals)) {
787 
788  SAVE_REL_OFFSET(offset);
789 
790  char *target = (char *) get_branch_target(relocated_ins, xptr, vals);
791 
792  KILL_REL_OFFSET();
793 
794  void *start, *end;
795  if (target < relocated_ins) {
796  start = target;
797  int branch_inst_len = xed_decoded_inst_get_length(xptr);
798  end = relocated_ins + branch_inst_len;
799  if (inside_protected_range(target)) {
800  add_protected_range(start, end);
801  return true;
802  }
803  }
804  }
805  }
806  return false;
807 }
808 
809 bool
810 range_contains_control_flow(void *vstart, void *vend)
811 {
812  xed_decoded_inst_t xedd_tmp;
813  xed_decoded_inst_t *xptr = &xedd_tmp;
814  xed_decoded_inst_zero_set_mode(xptr, &xed_machine_state);
815 
816  char *ins = (char *) vstart;
817  char *end = (char *) vend;
818  while (ins < end) {
819 
820  xed_error_enum_t xed_error = xed_decode(xptr, (uint8_t*) ins, 15);
821 
822  if (xed_error != XED_ERROR_NONE) {
823  ins++; /* skip this byte */
824  continue; /* continue onward ... */
825  }
826 
827  xed_iclass_enum_t xiclass = xed_decoded_inst_get_iclass(xptr);
828  switch(xiclass) {
829  // unconditional jump
830  case XED_ICLASS_JMP: case XED_ICLASS_JMP_FAR:
831 
832  // return
833  case XED_ICLASS_RET_FAR: case XED_ICLASS_RET_NEAR:
834 
835  // conditional branch
836  case XED_ICLASS_JB: case XED_ICLASS_JBE:
837  case XED_ICLASS_JL: case XED_ICLASS_JLE:
838  case XED_ICLASS_JNB: case XED_ICLASS_JNBE:
839  case XED_ICLASS_JNL: case XED_ICLASS_JNLE:
840  case XED_ICLASS_JNO: case XED_ICLASS_JNP:
841  case XED_ICLASS_JNS: case XED_ICLASS_JNZ:
842  case XED_ICLASS_JO: case XED_ICLASS_JP:
843  case XED_ICLASS_JRCXZ: case XED_ICLASS_JS:
844  case XED_ICLASS_JZ:
845  return true;
846 
847  default:
848  break;
849  }
850  ins += xed_decoded_inst_get_length(xptr);
851  }
852  return false;
853 }
854 
855 
856 static bool
857 validate_tail_call_from_jump(char *ins, long offset, xed_decoded_inst_t *xptr)
858 {
859  char *relocated_ins = ins + offset;
860  const xed_inst_t *xi = xed_decoded_inst_inst(xptr);
861  const xed_operand_t *op0 = xed_inst_operand(xi, 0);
862  xed_operand_enum_t op0_name = xed_operand_name(op0);
863  xed_operand_type_enum_t op0_type = xed_operand_type(op0);
864 
865  if (op0_name == XED_OPERAND_RELBR &&
866  op0_type == XED_OPERAND_TYPE_IMM_CONST) {
867  xed_operand_values_t *vals = xed_decoded_inst_operands(xptr);
868 
869  if (xed_operand_values_has_branch_displacement(vals)) {
870 
871  SAVE_REL_OFFSET(offset);
872 
873  char *target = (char *) get_branch_target(relocated_ins, xptr, vals);
874 
875  KILL_REL_OFFSET();
876 
877  if (target < relocated_ins) {
878  // backward jump; if this is a tail call, it should fall on a function
879  // entry already in the function entries table
880  if (query_function_entry(target)) return true;
881 
882  // we may be looking at a bogus jump in data;
883  // we must make sure that its target is in bounds before inspecting it.
884  if (!consider_possible_fn_address(target)) return false;
885  char *target_addr_in_memory = target - offset;
886 
887  xed_decoded_inst_t xtmp;
888  xed_decoded_inst_t *xptr = &xtmp;
889  xed_decoded_inst_zero_set_mode(xptr, &xed_machine_state);
890 
891  xed_error_enum_t xed_error =
892  xed_decode(xptr, (uint8_t*) target_addr_in_memory, 15);
893  if (xed_error != XED_ERROR_NONE) return false;
894 
895  xed_iclass_enum_t xiclass = xed_decoded_inst_get_iclass(xptr);
896  const xed_inst_t *xi = xed_decoded_inst_inst(xptr);
897 
898  switch(xiclass) {
899  case XED_ICLASS_PUSH:
900  case XED_ICLASS_PUSHFQ:
901  case XED_ICLASS_PUSHFD:
902  case XED_ICLASS_PUSHF:
903  {
904  const xed_operand_t *op0 = xed_inst_operand(xi, 0);
905  xed_operand_enum_t op0_name = xed_operand_name(op0);
906 
907  if (op0_name == XED_OPERAND_REG0) {
908  xed_reg_enum_t regname =
909  xed_decoded_inst_get_reg(xptr, op0_name);
910  if (x86_isReg_BP(regname)) {
911  add_stripped_function_entry(target, 1 /* support */);
912  return true;
913  }
914  }
915  }
916  return false;
917  case XED_ICLASS_ADD:
918  case XED_ICLASS_SUB:
919  {
920  const xed_operand_t* op0 = xed_inst_operand(xi,0);
921  const xed_operand_t* op1 = xed_inst_operand(xi,1);
922  xed_operand_enum_t op0_name = xed_operand_name(op0);
923 
924  if ((op0_name == XED_OPERAND_REG0)
925  && x86_isReg_SP(xed_decoded_inst_get_reg(xptr, op0_name))) {
926 
927  if (xed_operand_name(op1) == XED_OPERAND_IMM0) {
928  //-------------------------------------------------
929  // adjusting the stack pointer by a constant offset
930  //-------------------------------------------------
931  int sign = (xiclass == XED_ICLASS_ADD) ? 1 : -1;
932  long immedv = sign *
933  xed_decoded_inst_get_signed_immediate(xptr);
934  if (immedv < 0) {
935  add_stripped_function_entry(target, 1 /* support */);
936  return true;
937  }
938  }
939  }
940  }
941  return false;
942  default:
943  // not what is expected for the start of a routine.
944  return false;
945  }
946  } else {
947  return range_contains_control_flow(ins, target);
948  }
949  }
950  }
951  return false;
952 }
953 
954 
955 static bool
956 lea_has_zero_offset(xed_decoded_inst_t *xptr)
957 {
958  const xed_inst_t *xi = xed_decoded_inst_inst(xptr);
959  const xed_operand_t *op1 = xed_inst_operand(xi, 1);
960  xed_operand_enum_t op1_name = xed_operand_name(op1);
961  if (op1_name == XED_OPERAND_AGEN) {
962  int64_t offset = xed_decoded_inst_get_memory_displacement(xptr, 0);
963  if (offset == 0) return true;
964  }
965  return false;
966 }
967 
968 //
969 // utility to determine the address of the next instruction
970 //
971 static char*
972 xed_next(char* ins)
973 {
974  xed_decoded_inst_t xedd_tmp;
975  xed_decoded_inst_t* xptr = &xedd_tmp;
976  xed_error_enum_t xed_error;
977  int offset = 0;
978 
979  xed_decoded_inst_zero_set_mode(xptr, &xed_machine_state);
980  xed_decoded_inst_zero_keep_mode(xptr);
981 
982  xed_error = xed_decode(xptr, (uint8_t*) ins, 15);
983  if (xed_error == XED_ERROR_NONE) {
984  offset = xed_decoded_inst_get_length(xptr);
985  }
986  return ins + offset;
987 }
988 
989 static bool
990 is_mov_sp_2_bp(char* ins)
991 {
992  xed_decoded_inst_t xedd_tmp;
993  xed_decoded_inst_t* xptr = &xedd_tmp;
994  xed_error_enum_t xed_error;
995 
996  xed_decoded_inst_zero_set_mode(xptr, &xed_machine_state);
997  xed_decoded_inst_zero_keep_mode(xptr);
998 
999  xed_error = xed_decode(xptr, (uint8_t*) ins, 15);
1000  if (xed_error != XED_ERROR_NONE) return false;
1001 
1002  if (xed_decoded_inst_get_iclass(xptr) != XED_ICLASS_MOV)
1003  return false;
1004 
1005  const xed_inst_t *xi = xed_decoded_inst_inst(xptr);
1006  const xed_operand_t *op0 = xed_inst_operand(xi, 0);
1007  const xed_operand_t *op1 = xed_inst_operand(xi, 1);
1008 
1009  xed_operand_enum_t op0_name = xed_operand_name(op0);
1010  xed_operand_enum_t op1_name = xed_operand_name(op1);
1011 
1012  if ((op0_name == XED_OPERAND_REG0) && (op1_name == XED_OPERAND_REG1)) {
1013  //-------------------------------------------------------------------------
1014  // register-to-register move
1015  //-------------------------------------------------------------------------
1016  xed_reg_enum_t reg0 = xed_decoded_inst_get_reg(xptr, op0_name);
1017  xed_reg_enum_t reg1 = xed_decoded_inst_get_reg(xptr, op1_name);
1018  return x86_isReg_BP(reg0) && x86_isReg_SP(reg1);
1019  }
1020  return false;
1021 }
1022 
1023 static bool
1025 {
1026  return is_push_bp(ins) && is_mov_sp_2_bp(xed_next(ins));
1027 }
1028 
1029 static const size_t FRAMELESS_PROC_WINDOW = 8;
1030 
1031 static bool
1033 {
1034  xed_decoded_inst_t xedd_tmp;
1035  xed_decoded_inst_t* xptr = &xedd_tmp;
1036  xed_error_enum_t xed_error;
1037 
1038  xed_decoded_inst_zero_set_mode(xptr, &xed_machine_state);
1039  xed_decoded_inst_zero_keep_mode(xptr);
1040 
1041  for (size_t i=0; i < FRAMELESS_PROC_WINDOW; i++) {
1042  xed_error = xed_decode(xptr, (uint8_t*) ins, 15);
1043  if (xed_error != XED_ERROR_NONE) {
1044  ins++;
1045  continue;
1046  }
1047  if (xed_decoded_inst_get_iclass(xptr) == XED_ICLASS_MOV) {
1048  const xed_inst_t *xi = xed_decoded_inst_inst(xptr);
1049  const xed_operand_t *op0 = xed_inst_operand(xi, 0);
1050  const xed_operand_t *op1 = xed_inst_operand(xi, 1);
1051 
1052  xed_operand_enum_t op0_name = xed_operand_name(op0);
1053  xed_operand_enum_t op1_name = xed_operand_name(op1);
1054 
1055  if ((op0_name == XED_OPERAND_REG0) && (op1_name == XED_OPERAND_REG1)) {
1056  //-------------------------------------------------------------------------
1057  // register-to-register move
1058  //-------------------------------------------------------------------------
1059  xed_reg_enum_t reg0 = xed_decoded_inst_get_reg(xptr, op0_name);
1060  if (x86_isReg_BP(reg0))
1061  return true;
1062  }
1063  }
1064  ins += xed_decoded_inst_get_length(xptr);
1065  }
1066  return false;
1067 }
1068 
1069 //
1070 // Heuristic for identifying common frameless procedure pattern:
1071 // push bp
1072 // ...
1073 // mov SOME_REG, bp
1074 //
1075 // In other words, a push of bp followed by overwriting bp with some other register
1076 // indicates a (probable) start of a procedure.
1077 //
1078 static bool
1080 {
1081  return is_push_bp(ins) && ins_seq_has_reg_move_to_bp(xed_next(ins));
1082 }
1083 
1084 static bool
1085 nextins_looks_like_fn_start(char *ins, long offset, xed_decoded_inst_t *xptrin)
1086 {
1087  xed_decoded_inst_t xedd_tmp;
1088  xed_decoded_inst_t *xptr = &xedd_tmp;
1089  xed_error_enum_t xed_error;
1090 
1091  ins = ins + xed_decoded_inst_get_length(xptrin);
1092 
1093  if (inside_protected_range(ins + offset)) return false;
1094 
1095  xed_decoded_inst_zero_set_mode(xptr, &xed_machine_state);
1096 
1097  for(;;) {
1098  xed_decoded_inst_zero_keep_mode(xptr);
1099  xed_error = xed_decode(xptr, (uint8_t*) ins, 15);
1100 
1101  if (xed_error != XED_ERROR_NONE) return false;
1102 
1103  xed_iclass_enum_t xiclass = xed_decoded_inst_get_iclass(xptr);
1104  switch(xiclass) {
1105  case XED_ICLASS_LEA:
1106  if (!lea_has_zero_offset(xptr)) return false;
1107  // gcc seems to use 0 offset lea as padding between functions
1108  // treat it as a nop
1109 
1110  case XED_ICLASS_NOP: case XED_ICLASS_NOP2: case XED_ICLASS_NOP3:
1111  case XED_ICLASS_NOP4: case XED_ICLASS_NOP5: case XED_ICLASS_NOP6:
1112  case XED_ICLASS_NOP7: case XED_ICLASS_NOP8: case XED_ICLASS_NOP9:
1113  // nop: move to the next instruction
1114  ins = ins + xed_decoded_inst_get_length(xptr);
1115  break;
1116  case XED_ICLASS_PUSH:
1117  case XED_ICLASS_PUSHFQ:
1118  case XED_ICLASS_PUSHFD:
1119  case XED_ICLASS_PUSHF:
1121  break;
1122 
1123  case XED_ICLASS_ADD:
1124  case XED_ICLASS_SUB:
1125  {
1126  const xed_inst_t *xi = xed_decoded_inst_inst(xptr);
1127  const xed_operand_t* op0 = xed_inst_operand(xi,0);
1128  const xed_operand_t* op1 = xed_inst_operand(xi,1);
1129  xed_operand_enum_t op0_name = xed_operand_name(op0);
1130 
1131  if ((op0_name == XED_OPERAND_REG0) &&
1132  (x86_isReg_SP(xed_decoded_inst_get_reg(xptr, op0_name)))) {
1133 
1134  if (xed_operand_name(op1) == XED_OPERAND_IMM0) {
1135  //------------------------------------------------------------------
1136  // we are adjusting the stack pointer by a constant offset
1137  //------------------------------------------------------------------
1138  int sign = (xiclass == XED_ICLASS_ADD) ? 1 : -1;
1139  long immedv = sign * xed_decoded_inst_get_signed_immediate(xptr);
1140  if (immedv < 0) {
1141  add_stripped_function_entry(ins + offset, 1 /* support */);
1142  return true;
1143  }
1144  }
1145  }
1146  }
1147  return false;
1148  default:
1149  // not a nop, or what is expected for the start of a routine.
1150  return false;
1151  break;
1152  }
1153  }
1154 
1155  return false;
1156 }
1157 
1158 
1159 static void
1160 process_branch(char *ins, long offset, xed_decoded_inst_t *xptr, char* vstart, char* vend)
1161 {
1162  char *relocated_ins = ins + offset;
1163  const xed_inst_t *xi = xed_decoded_inst_inst(xptr);
1164  const xed_operand_t *op0 = xed_inst_operand(xi, 0);
1165  xed_operand_enum_t op0_name = xed_operand_name(op0);
1166  xed_operand_type_enum_t op0_type = xed_operand_type(op0);
1167 
1168  if (op0_name == XED_OPERAND_RELBR &&
1169  op0_type == XED_OPERAND_TYPE_IMM_CONST) {
1170  xed_operand_values_t *vals = xed_decoded_inst_operands(xptr);
1171 
1172  if (xed_operand_values_has_branch_displacement(vals)) {
1173 
1174  SAVE_REL_OFFSET(offset);
1175 
1176  char *target = (char *) get_branch_target(relocated_ins, xptr, vals);
1177 
1178  KILL_REL_OFFSET();
1179 
1180  //
1181  // if branch target is not within bounds, then this branch instruction is bogus ....
1182  //
1183  if (! (((vstart + offset) <= target) && (target <= (vend + offset)))) {
1184  return;
1185  }
1186  void *start, *end;
1187  if (target < relocated_ins) {
1188  unsigned char *tloc = (unsigned char *) target - offset;
1189  for (; is_padding(*(tloc-1)); tloc--) {
1190  // extend branch range to before padding
1191  target--;
1192  }
1193 
1194  start = target;
1195  // protect to end of branch instruction
1196  int branch_inst_len = xed_decoded_inst_get_length(xptr);
1197  end = relocated_ins + branch_inst_len;
1198  } else {
1199  start = relocated_ins;
1200  //-----------------------------------------------------
1201  // add one to ensure that the branch target is part of
1202  // the "covered" range
1203  //-----------------------------------------------------
1204  end = ((char *) target) + 1;
1205  }
1206  add_protected_range(start, end);
1207  }
1208  }
1209 }
1210 
1211 static int
1212 mem_below_rsp_or_rbp(xed_decoded_inst_t *xptr, int oindex)
1213 {
1214  xed_reg_enum_t basereg = xed_decoded_inst_get_base_reg(xptr, oindex);
1215  if (x86_isReg_BP(basereg)) {
1216  return 1;
1217  } else if (x86_isReg_SP(basereg)) {
1218  int64_t offset =
1219  xed_decoded_inst_get_memory_displacement(xptr, oindex);
1220  if (offset > 0) {
1221  return 1;
1222  }
1223  } else if (x86_isReg_AX(basereg)) {
1224  return 1;
1225  }
1226  return 0;
1227 }
1228 
1229 static bool
1230 inst_accesses_callers_mem(xed_decoded_inst_t *xptr)
1231 {
1232  // if the instruction accesses memory below the rsp or rbp, this is
1233  // not a valid first instruction for a routine. if this is the first
1234  // instruction in a routine, this must be manipulating values in the
1235  // caller's frame, not its own.
1236  int noperands = xed_decoded_inst_number_of_memory_operands(xptr);
1237  int not_my_mem = 0;
1238  switch (noperands) {
1239  case 2: not_my_mem |= mem_below_rsp_or_rbp(xptr, 1);
1240  case 1: not_my_mem |= mem_below_rsp_or_rbp(xptr, 0);
1241  case 0:
1242  break;
1243  default:
1244  assert(0 && "unexpected number of memory operands");
1245  }
1246  if (not_my_mem) return true;
1247  return false;
1248 }
1249 
1250 
1251 static bool
1252 from_ax_reg(xed_decoded_inst_t *xptr)
1253 {
1254  static xed_operand_enum_t regops[] = { XED_OPERAND_REG0, XED_OPERAND_REG1 };
1255  const xed_inst_t *xi = xed_decoded_inst_inst(xptr);
1256  int noperands = xed_decoded_inst_noperands(xptr);
1257 
1258  if (noperands > 2) noperands = 2; // we don't care about more than two operands
1259  for (int opid = 0; opid < noperands; opid++) {
1260  const xed_operand_t *op = xed_inst_operand(xi, opid);
1261  xed_operand_enum_t op_name = xed_operand_name(op);
1262  if (op_name == regops[opid]) {
1263  xed_reg_enum_t regname = xed_decoded_inst_get_reg(xptr, op_name);
1264  if ((regname == XED_REG_RAX) || (regname == XED_REG_EAX) || (regname == XED_REG_AX)) {
1265  // operand may perform a read of rax/eax/ax
1266  switch(xed_operand_rw(op)) {
1267  case XED_OPERAND_ACTION_R: // read
1268  // case XED_OPERAND_ACTION_RW: // read and written: skip this case - xor %eax, %eax is OK
1269  case XED_OPERAND_ACTION_RCW: // read and conditionlly written
1270  case XED_OPERAND_ACTION_CR: // conditionally read
1271  case XED_OPERAND_ACTION_CRW: // conditionlly read, always written
1272  return true;
1273  default:
1274  return false;
1275  }
1276  }
1277  }
1278  }
1279  return false;
1280 }
1281 
1282 
1283 static bool
1284 is_null(unsigned char *ins, int n)
1285 {
1286  unsigned char result = 0;
1287  unsigned char *end = ins + n;
1288  while (ins < end) result |= *ins++;
1289  if (result == 0) return true;
1290  else return false;
1291 }
1292 
1293 static bool
1294 is_breakpoint(xed_decoded_inst_t *xptr)
1295 {
1296  xed_iclass_enum_t xiclass = xed_decoded_inst_get_iclass(xptr);
1297  switch(xiclass) {
1298  case XED_ICLASS_INT:
1299  case XED_ICLASS_INT1:
1300  case XED_ICLASS_INT3:
1301  return true;
1302  default:
1303  break;
1304  }
1305  return false;
1306 }
1307 
1308 static bool
1309 invalid_routine_start(unsigned char *ins)
1310 {
1311  xed_decoded_inst_t xdi;
1312  xed_decoded_inst_t *xptr = &xdi;
1313 
1314  xed_decoded_inst_zero_set_mode(xptr, &xed_machine_state);
1315  xed_error_enum_t xed_error = xed_decode(xptr, (uint8_t*) ins, 15);
1316 
1317  if (xed_error != XED_ERROR_NONE) return true;
1318 
1319  if (is_null(ins, xed_decoded_inst_get_length(xptr))) return true;
1320  if (inst_accesses_callers_mem(xptr)) return true;
1321  if (from_ax_reg(xptr)) return true;
1322  if (is_breakpoint(xptr)) return true;
1323 
1324  return false;
1325 }
1326 
1327 
1328 void x86_dump_ins(void *ins)
1329 {
1330  xed_decoded_inst_t xedd;
1331  xed_decoded_inst_t *xptr = &xedd;
1332  xed_error_enum_t xed_error;
1333  char inst_buf[1024];
1334 
1335  xed_decoded_inst_zero_set_mode(xptr, &xed_machine_state);
1336  xed_error = xed_decode(xptr, (uint8_t*) ins, 15);
1337 
1338  if (xed_error == XED_ERROR_NONE) {
1339  xed_decoded_inst_dump_xed_format(xptr, inst_buf, sizeof(inst_buf), (uint64_t) ins);
1340  printf("(%p, %d bytes, %s) %s \n" , ins, xed_decoded_inst_get_length(xptr),
1341  xed_iclass_enum_t2str(xed_decoded_inst_get_iclass(xptr)), inst_buf);
1342  } else {
1343  printf("x86_dump_ins: xed decode addr=%p, error = %d\n", ins, xed_error);
1344  }
1345 }
1346 
1347 
1348 // #define DEBUG_ADDSUB
1349 
1350 static void
1351 addsub(char *ins, xed_decoded_inst_t *xptr, xed_iclass_enum_t iclass, long ins_offset)
1352 {
1353  const xed_inst_t *xi = xed_decoded_inst_inst(xptr);
1354  const xed_operand_t* op0 = xed_inst_operand(xi,0);
1355  const xed_operand_t* op1 = xed_inst_operand(xi,1);
1356  xed_operand_enum_t op0_name = xed_operand_name(op0);
1357  static long prologue_offset = 0;
1358 
1359  if ((op0_name == XED_OPERAND_REG0) &&
1360  x86_isReg_SP(xed_decoded_inst_get_reg(xptr, op0_name))) {
1361 
1362  if (xed_operand_name(op1) == XED_OPERAND_IMM0) {
1363  //---------------------------------------------------------------------------
1364  // we are adjusting the stack pointer by a constant offset
1365  //---------------------------------------------------------------------------
1366  int sign = (iclass == XED_ICLASS_ADD) ? 1 : -1;
1367  long immedv = sign * xed_decoded_inst_get_signed_immediate(xptr);
1368  if (immedv < 0) {
1369  prologue_start = ins;
1370  prologue_offset = -immedv;
1371 #ifdef DEBUG_ADDSUB
1372  fprintf(stderr,"prologue %ld\n", immedv);
1373 #endif
1374  } else {
1375 #ifdef DEBUG_ADDSUB
1376  fprintf(stderr,"epilogue %ld\n", immedv);
1377 #endif
1378  if (immedv == prologue_offset) {
1379  // add one to both endpoints
1380  // -- ensure that add/sub in the prologue IS NOT part of the range
1381  // (it may be the first instruction in the function - we don't want
1382  // to prevent it from starting a function)
1383  // -- ensure that add/sub in the epilogue IS part of the range
1384  add_protected_range(prologue_start + ins_offset + 1,
1385  ins + ins_offset + 1);
1386 #ifdef DEBUG_ADDSUB
1387  char *end = ins + 1;
1388  fprintf(stderr,"range [%p, %p] offset %ld\n",
1389  prologue_start + ins_offset, end + ins_offset, immedv);
1390 #endif
1391  }
1392  }
1393  }
1394  }
1395 }
1396 
1397 
1398 // don't track the push, track the move rsp to rbp or esp to ebp
1399 static void
1400 process_move(char *ins, xed_decoded_inst_t *xptr, long ins_offset)
1401 {
1402  const xed_inst_t *xi = xed_decoded_inst_inst(xptr);
1403  const xed_operand_t *op0 = xed_inst_operand(xi, 0);
1404  const xed_operand_t *op1 = xed_inst_operand(xi, 1);
1405 
1406  xed_operand_enum_t op0_name = xed_operand_name(op0);
1407  xed_operand_enum_t op1_name = xed_operand_name(op1);
1408 
1409  if ((op0_name == XED_OPERAND_REG0) && (op1_name == XED_OPERAND_REG1)) {
1410  //-------------------------------------------------------------------------
1411  // register-to-register move
1412  //-------------------------------------------------------------------------
1413  xed_reg_enum_t reg0 = xed_decoded_inst_get_reg(xptr, op0_name);
1414  xed_reg_enum_t reg1 = xed_decoded_inst_get_reg(xptr, op1_name);
1415  if ((x86_isReg_BP(reg0)) && (x86_isReg_SP(reg1))) {
1416  //=========================================================================
1417  // instruction: initialize BP with value of SP to set up a frame pointer
1418  //=========================================================================
1419  set_rbp = ins;
1420  }
1421  }
1422 }
1423 
1424 
1425 static void
1426 process_push(char *ins, xed_decoded_inst_t *xptr, long ins_offset)
1427 {
1428  const xed_inst_t *xi = xed_decoded_inst_inst(xptr);
1429  const xed_operand_t *op0 = xed_inst_operand(xi, 0);
1430  xed_operand_enum_t op0_name = xed_operand_name(op0);
1431 
1432  if (op0_name == XED_OPERAND_REG0) {
1433  xed_reg_enum_t regname = xed_decoded_inst_get_reg(xptr, op0_name);
1434  if (x86_isReg_BP(regname)) {
1435  push_rbp = ins;
1436  // JMC + MIKE: assume that a push when there is only weak evidence of a fn start
1437  // might be a potential function entry
1438 #if 0
1439  if (fn_start == WEAK_FN){
1440  add_stripped_function_entry(ins + ins_offset, 1 /* support */);
1441  fn_start == MODERATE_FN;
1442  }
1443 #endif
1444  } else {
1445  push_other = ins;
1446  push_other_reg = regname;
1447  }
1448  }
1449 }
1450 
1451 
1452 static void
1453 process_pop(char *ins, xed_decoded_inst_t *xptr, long ins_offset)
1454 {
1455  const xed_inst_t *xi = xed_decoded_inst_inst(xptr);
1456  const xed_operand_t *op0 = xed_inst_operand(xi, 0);
1457  xed_operand_enum_t op0_name = xed_operand_name(op0);
1458 
1459  if (op0_name == XED_OPERAND_REG0) {
1460  xed_reg_enum_t regname = xed_decoded_inst_get_reg(xptr, op0_name);
1461  if (x86_isReg_BP(regname)) {
1462  if (push_rbp) {
1463  add_protected_range(push_rbp + ins_offset + 1, ins + ins_offset + 1);
1464  } else {
1465  if (push_other) {
1466  if (push_other_reg == regname) {
1467  if (push_other) {
1468  add_protected_range(push_other + ins_offset + 1,
1469  ins + ins_offset + 1);
1470  }
1471  } else {
1472  // it must match some push. assume the latest.
1473  char *push_latest = (push_other > push_rbp) ? push_other : push_rbp;
1474  // ... unless we've started to see bad instructions, which makes
1475  // it likely that we're walking through data
1476  if (push_latest > last_bad)
1477  if (push_latest) {
1478  add_protected_range(push_latest + ins_offset + 1,
1479  ins + ins_offset + 1);
1480  }
1481  }
1482  }
1483  }
1484  }
1485  }
1486 }
1487 
1488 
1489 static void
1490 process_enter(char *ins, long ins_offset)
1491 {
1492  set_rbp = ins;
1493 }
1494 
1495 
1496 static void
1497 process_leave(char *ins, long ins_offset)
1498 {
1499  char *save_rbp = (set_rbp > push_rbp) ? set_rbp : push_rbp;
1500  add_protected_range(save_rbp + ins_offset + 1, ins + ins_offset + 1);
1501 }
1502 
static const size_t WINDOW
static bool is_sub_immed_sp(char *ins, char **next)
static char * push_rbp
bool weak
Definition: amd-xop.h:9
static bool is_push_bp(char *ins)
static bool skip_padding(unsigned char **ins)
void adv_amd_decode(amd_decode_t *stat, void *ins)
Definition: amd-xop.c:227
static int mem_below_rsp_or_rbp(xed_decoded_inst_t *xptr, int oindex)
static bool lea_has_zero_offset(xed_decoded_inst_t *xptr)
static void * get_branch_target(char *ins, xed_decoded_inst_t *xptr, xed_operand_values_t *vals)
static void process_push(char *ins, xed_decoded_inst_t *xptr, long ins_offset)
bool query_function_entry(void *addr)
static bool contains_bp_save(char *ins)
static void addsub(char *ins, xed_decoded_inst_t *xptr, xed_iclass_enum_t iclass, long ins_offset)
static bool is_sub_immed_prologue(char *ins)
static bool invalid_routine_start(unsigned char *ins)
static void process_call(char *ins, long offset, xed_decoded_inst_t *xptr, void *start, void *end)
void add_stripped_function_entry(void *addr, int call_count)
static int is_padding(int c)
bool success
Definition: amd-xop.h:8
xed_iclass_enum_t xed_iclass(char *ins)
static void process_enter(char *ins, long ins_offset)
static void process_leave(char *ins, long ins_offset)
static char * set_rbp
void x86_dump_ins(void *ins)
bool consider_possible_fn_address(void *addr)
static bool bkwd_jump_into_protected_range(char *ins, long offset, xed_decoded_inst_t *xptr)
static bool ins_seq_is_std_frame(char *ins)
static void process_branch(char *ins, long offset, xed_decoded_inst_t *xptr, char *vstart, char *vend)
#define RELOCATE(u, offset)
static xed_state_t xed_machine_state
void add_function_entry(void *addr, const string *comment, bool isvisible, int call_count)
static bool ins_seq_has_reg_move_to_bp(char *ins)
static void process_pop(char *ins, xed_decoded_inst_t *xptr, long ins_offset)
static xed_reg_enum_t push_other_reg
static const size_t FRAMELESS_PROC_WINDOW
static char * xed_next(char *ins)
static bool is_mov_sp_2_bp(char *ins)
static bool inst_accesses_callers_mem(xed_decoded_inst_t *xptr)
void process_range_init()
static bool is_2step_push_bp(char *ins)
static void process_move(char *ins, xed_decoded_inst_t *xptr, long ins_offset)
DiscoverFnTy
Definition: code-ranges.h:52
static xed_decoded_inst_t xedd
Definition: x86ISAXed.cpp:92
void entries_in_range(void *start, void *end, vector< void *> &result)
#define iclass(xptr)
Definition: x86-decoder.h:69
int inside_protected_range(void *addr)
#define KILL_REL_OFFSET()
static bool is_breakpoint(xed_decoded_inst_t *xptr)
static char * last_bad
size_t len
Definition: amd-xop.h:10
static char * push_other
static bool is_push_bp_seq(char *ins)
static bool from_ax_reg(xed_decoded_inst_t *xptr)
static bool contains_bp_save_window(char *ins, size_t window)
static bool is_null(unsigned char *ins, int n)
static xed_state_t dbg_xed_machine_state
static bool ins_seq_is_common_frameless_proc(char *ins)
#define NULL
Definition: ElfHelper.cpp:85
static bool nextins_looks_like_fn_start(char *ins, long offset, xed_decoded_inst_t *xptrin)
void process_range(const char *name, long offset, void *vstart, void *vend, DiscoverFnTy fn_discovery)
#define SAVE_REL_OFFSET(offset)
<!-- ********************************************************************--> n<!-- HPCToolkit Experiment DTD --> n<!-- Version 2.1 --> n<!-- ********************************************************************--> n<!ELEMENT HPCToolkitExperiment(Header,(SecCallPathProfile|SecFlatProfile) *)> n<!ATTLIST HPCToolkitExperiment\n version CDATA #REQUIRED > n n<!-- ******************************************************************--> n n<!-- Info/NV:flexible name-value pairs:(n) ame;(t) ype;(v) alue --> n<!ELEMENT Info(NV *)> n<!ATTLIST Info\n n CDATA #IMPLIED > n<!ELEMENT NV EMPTY > n<!ATTLIST NV\n n CDATA #REQUIRED\n t CDATA #IMPLIED\n v CDATA #REQUIRED > n n<!-- ******************************************************************--> n<!-- Header --> n<!-- ******************************************************************--> n<!ELEMENT Header(Info *)> n<!ATTLIST Header\n n CDATA #REQUIRED > n n<!-- ******************************************************************--> n<!-- Section Header --> n<!-- ******************************************************************--> n<!ELEMENT SecHeader(MetricTable?, MetricDBTable?, TraceDBTable?, LoadModuleTable?, FileTable?, ProcedureTable?, Info *)> n n<!-- MetricTable:--> n<!ELEMENT MetricTable(Metric) * > n n<!-- Metric:(i) d;(n) ame --> n<!--(v) alue-type:transient type of values --> n<!--(t) ype:persistent type of metric --> n<!-- fmt:format;show;--> n<!ELEMENT Metric(MetricFormula *, Info?)> n<!ATTLIST Metric\n i CDATA #REQUIRED\n n CDATA #REQUIRED\n es CDATA #IMPLIED\n em CDATA #IMPLIED\n ep CDATA #IMPLIED\n v(raw|final|derived-incr|derived) \"raw\\ t (inclusive|exclusive|nil) \nil\\ partner CDATA #IMPLIED\ fmt CDATA #IMPLIED\ show (1|0) \1\\ show-percent (1|0) \1> n n<!-- MetricFormula represents derived metrics: (t)ype; (frm): formula --> n<!ELEMENT MetricFormula (Info?)> n<!ATTLIST MetricFormula\ t (combine|finalize) \finalize\\ i CDATA #IMPLIED\ frm CDATA #REQUIRED> n n<!-- Metric data, used in sections: (n)ame [from Metric]; (v)alue --> n<!ELEMENT M EMPTY> n<!ATTLIST M\ n CDATA #REQUIRED\ v CDATA #REQUIRED> n n<!-- MetricDBTable: --> n<!ELEMENT MetricDBTable (MetricDB)*> n n<!-- MetricDB: (i)d; (n)ame --> n<!-- (t)ype: persistent type of metric --> n<!-- db-glob: file glob describing files in metric db --> n<!-- db-id: id within metric db --> n<!-- db-num-metrics: number of metrics in db --> n<!-- db-header-sz: size (in bytes) of a db file header --> n<!ELEMENT MetricDB EMPTY> n<!ATTLIST MetricDB\ i CDATA #REQUIRED\ n CDATA #REQUIRED\ t (inclusive|exclusive|nil) \nil\\ partner CDATA #IMPLIED\ db-glob CDATA #IMPLIED\ db-id CDATA #IMPLIED\ db-num-metrics CDATA #IMPLIED\ db-header-sz CDATA #IMPLIED> n n<!-- TraceDBTable: --> n<!ELEMENT TraceDBTable (TraceDB)> n n<!-- TraceDB: (i)d --> n<!-- db-min-time: min beginning time stamp (global) --> n<!-- db-max-time: max ending time stamp (global) --> n<!ELEMENT TraceDB EMPTY> n<!ATTLIST TraceDB\ i CDATA #REQUIRED\ db-glob CDATA #IMPLIED\ db-min-time CDATA #IMPLIED\ db-max-time CDATA #IMPLIED\ db-header-sz CDATA #IMPLIED> n n<!-- LoadModuleTable assigns a short name to a load module --> n<!ELEMENT LoadModuleTable (LoadModule)*> n n<!ELEMENT LoadModule (Info?)> n<!ATTLIST LoadModule\ i CDATA #REQUIRED\ n CDATA #REQUIRED> n n<!-- FileTable assigns a short name to a file --> n<!ELEMENT FileTable (File)*> n n<!ELEMENT File (Info?)> n<!ATTLIST File\ i CDATA #REQUIRED\ n CDATA #REQUIRED> n n<!-- ProcedureTable assigns a short name to a procedure --> n<!ELEMENT ProcedureTable (Procedure)*> n n<!-- Info/NV: flexible name-value pairs: (n)ame; (t)ype; (v)alue --> n<!-- f: family of the procedure (fake, root, ...)--> n<!ELEMENT Procedure (Info?)> n<!ATTLIST Procedure\ i CDATA #REQUIRED\ n CDATA #REQUIRED\ f CDATA #IMPLIED> n n<!-- ****************************************************************** --> n<!-- Section: Call path profile --> n<!-- ****************************************************************** --> n<!ELEMENT SecCallPathProfile (SecHeader, SecCallPathProfileData)> n<!ATTLIST SecCallPathProfile\ i CDATA #REQUIRED\ n CDATA #REQUIRED> n n<!ELEMENT SecCallPathProfileData (PF|M)*> n<!-- Procedure frame --> n<!-- (i)d: unique identifier for cross referencing --> n<!-- (s)tatic scope id --> n<!-- (n)ame: a string or an id in ProcedureTable --> n<!-- (lm) load module: a string or an id in LoadModuleTable --> n<!-- (f)ile name: a string or an id in LoadModuleTable --> n<!-- (l)ine range: \beg-end\ (inclusive range) --> n<!-- (a)lien: whether frame is alien to enclosing P --> n<!-- (str)uct: hpcstruct node id --> n<!-- (t)ype: hpcrun node type: memory access, variable declaration, ... --> n<!-- (v)ma-range-set: \{[beg-end), [beg-end)...}\ --> n<!ELEMENT PF (PF|Pr|L|C|S|M)*> n<!ATTLIST PF\ i CDATA #IMPLIED\ s CDATA #IMPLIED\ n CDATA #REQUIRED\ lm CDATA #IMPLIED\ f CDATA #IMPLIED\ l CDATA #IMPLIED\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n<!-- Procedure (static): GOAL: replace with 'P' --> n<!ELEMENT Pr (Pr|L|C|S|M)*> n<!ATTLIST Pr\ i CDATA #IMPLIED\ s CDATA #IMPLIED\ n CDATA #REQUIRED\ lm CDATA #IMPLIED\ f CDATA #IMPLIED\ l CDATA #IMPLIED\ a (1|0) \0\\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n<!-- Callsite (a special StatementRange) --> n<!ELEMENT C (PF|M)*> n<!ATTLIST C\ i CDATA #IMPLIED\ s CDATA #IMPLIED\ l CDATA #IMPLIED\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n n<!-- ****************************************************************** --> n<!-- Section: Flat profile --> n<!-- ****************************************************************** --> n<!ELEMENT SecFlatProfile (SecHeader, SecFlatProfileData)> n<!ATTLIST SecFlatProfile\ i CDATA #REQUIRED\ n CDATA #REQUIRED> n n<!ELEMENT SecFlatProfileData (LM|M)*> n<!-- Load module: (i)d; (n)ame; (v)ma-range-set --> n<!ELEMENT LM (F|P|M)*> n<!ATTLIST LM\ i CDATA #IMPLIED\ n CDATA #REQUIRED\ v CDATA #IMPLIED> n<!-- File --> n<!ELEMENT F (P|L|S|M)*> n<!ATTLIST F\ i CDATA #IMPLIED\ n CDATA #REQUIRED> n<!-- Procedure (Note 1) --> n<!ELEMENT P (P|A|L|S|C|M)*> n<!ATTLIST P\ i CDATA #IMPLIED\ n CDATA #REQUIRED\ l CDATA #IMPLIED\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n<!-- Alien (Note 1) --> n<!ELEMENT A (A|L|S|C|M)*> n<!ATTLIST A\ i CDATA #IMPLIED\ f CDATA #IMPLIED\ n CDATA #IMPLIED\ l CDATA #IMPLIED\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n<!-- Loop (Note 1,2) --> n<!ELEMENT L (A|Pr|L|S|C|M)*> n<!ATTLIST L\ i CDATA #IMPLIED\ s CDATA #IMPLIED\ l CDATA #IMPLIED\ f CDATA #IMPLIED\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n<!-- Statement (Note 2) --> n<!-- (it): trace record identifier --> n<!ELEMENT S (S|M)*> n<!ATTLIST S\ i CDATA #IMPLIED\ it CDATA #IMPLIED\ s CDATA #IMPLIED\ l CDATA #IMPLIED\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n<!-- Note 1: Contained Cs may not contain PFs --> n<!-- Note 2: The 's' attribute is not used for flat profiles --> n
bool range_contains_control_flow(void *vstart, void *vend)
static void after_unconditional(char *ins, long offset, xed_decoded_inst_t *xptr)
void add_protected_range(void *start, void *end)
static bool validate_tail_call_from_jump(char *ins, long offset, xed_decoded_inst_t *xptr)
bool contains_function_entry(void *address)
static char * prologue_start