trace.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (c) 2025, Oracle and/or its affiliates.
  4. */
  5. #include <objtool/trace.h>
  6. bool trace;
  7. int trace_depth;
  8. /*
  9. * Macros to trace CFI state attributes changes.
  10. */
  11. #define TRACE_CFI_ATTR(attr, prev, next, fmt, ...) \
  12. ({ \
  13. if ((prev)->attr != (next)->attr) \
  14. TRACE("%s=" fmt " ", #attr, __VA_ARGS__); \
  15. })
  16. #define TRACE_CFI_ATTR_BOOL(attr, prev, next) \
  17. TRACE_CFI_ATTR(attr, prev, next, \
  18. "%s", (next)->attr ? "true" : "false")
  19. #define TRACE_CFI_ATTR_NUM(attr, prev, next, fmt) \
  20. TRACE_CFI_ATTR(attr, prev, next, fmt, (next)->attr)
  21. #define CFI_REG_NAME_MAXLEN 16
  22. /*
  23. * Return the name of a register. Note that the same static buffer
  24. * is returned if the name is dynamically generated.
  25. */
  26. static const char *cfi_reg_name(unsigned int reg)
  27. {
  28. static char rname_buffer[CFI_REG_NAME_MAXLEN];
  29. const char *rname;
  30. switch (reg) {
  31. case CFI_UNDEFINED:
  32. return "<undefined>";
  33. case CFI_CFA:
  34. return "cfa";
  35. case CFI_SP_INDIRECT:
  36. return "(sp)";
  37. case CFI_BP_INDIRECT:
  38. return "(bp)";
  39. }
  40. if (reg < CFI_NUM_REGS) {
  41. rname = arch_reg_name[reg];
  42. if (rname)
  43. return rname;
  44. }
  45. if (snprintf(rname_buffer, CFI_REG_NAME_MAXLEN, "r%d", reg) == -1)
  46. return "<error>";
  47. return (const char *)rname_buffer;
  48. }
  49. /*
  50. * Functions and macros to trace CFI registers changes.
  51. */
  52. static void trace_cfi_reg(const char *prefix, int reg, const char *fmt,
  53. int base_prev, int offset_prev,
  54. int base_next, int offset_next)
  55. {
  56. char *rname;
  57. if (base_prev == base_next && offset_prev == offset_next)
  58. return;
  59. if (prefix)
  60. TRACE("%s:", prefix);
  61. if (base_next == CFI_UNDEFINED) {
  62. TRACE("%1$s=<undef> ", cfi_reg_name(reg));
  63. } else {
  64. rname = strdup(cfi_reg_name(reg));
  65. TRACE(fmt, rname, cfi_reg_name(base_next), offset_next);
  66. free(rname);
  67. }
  68. }
  69. static void trace_cfi_reg_val(const char *prefix, int reg,
  70. int base_prev, int offset_prev,
  71. int base_next, int offset_next)
  72. {
  73. trace_cfi_reg(prefix, reg, "%1$s=%2$s%3$+d ",
  74. base_prev, offset_prev, base_next, offset_next);
  75. }
  76. static void trace_cfi_reg_ref(const char *prefix, int reg,
  77. int base_prev, int offset_prev,
  78. int base_next, int offset_next)
  79. {
  80. trace_cfi_reg(prefix, reg, "%1$s=(%2$s%3$+d) ",
  81. base_prev, offset_prev, base_next, offset_next);
  82. }
  83. #define TRACE_CFI_REG_VAL(reg, prev, next) \
  84. trace_cfi_reg_val(NULL, reg, prev.base, prev.offset, \
  85. next.base, next.offset)
  86. #define TRACE_CFI_REG_REF(reg, prev, next) \
  87. trace_cfi_reg_ref(NULL, reg, prev.base, prev.offset, \
  88. next.base, next.offset)
  89. void trace_insn_state(struct instruction *insn, struct insn_state *sprev,
  90. struct insn_state *snext)
  91. {
  92. struct cfi_state *cprev, *cnext;
  93. int i;
  94. if (!memcmp(sprev, snext, sizeof(struct insn_state)))
  95. return;
  96. cprev = &sprev->cfi;
  97. cnext = &snext->cfi;
  98. disas_print_insn(stderr, objtool_disas_ctx, insn,
  99. trace_depth - 1, "state: ");
  100. /* print registers changes */
  101. TRACE_CFI_REG_VAL(CFI_CFA, cprev->cfa, cnext->cfa);
  102. for (i = 0; i < CFI_NUM_REGS; i++) {
  103. TRACE_CFI_REG_VAL(i, cprev->vals[i], cnext->vals[i]);
  104. TRACE_CFI_REG_REF(i, cprev->regs[i], cnext->regs[i]);
  105. }
  106. /* print attributes changes */
  107. TRACE_CFI_ATTR_NUM(stack_size, cprev, cnext, "%d");
  108. TRACE_CFI_ATTR_BOOL(drap, cprev, cnext);
  109. if (cnext->drap) {
  110. trace_cfi_reg_val("drap", cnext->drap_reg,
  111. cprev->drap_reg, cprev->drap_offset,
  112. cnext->drap_reg, cnext->drap_offset);
  113. }
  114. TRACE_CFI_ATTR_BOOL(bp_scratch, cprev, cnext);
  115. TRACE_CFI_ATTR_NUM(instr, sprev, snext, "%d");
  116. TRACE_CFI_ATTR_NUM(uaccess_stack, sprev, snext, "%u");
  117. TRACE("\n");
  118. insn->trace = 1;
  119. }
  120. void trace_alt_begin(struct instruction *orig_insn, struct alternative *alt,
  121. char *alt_name)
  122. {
  123. struct instruction *alt_insn;
  124. char suffix[2];
  125. alt_insn = alt->insn;
  126. if (alt->type == ALT_TYPE_EX_TABLE) {
  127. /*
  128. * When there is an exception table then the instruction
  129. * at the original location is executed but it can cause
  130. * an exception. In that case, the execution will be
  131. * redirected to the alternative instruction.
  132. *
  133. * The instruction at the original location can have
  134. * instruction alternatives, so we just print the location
  135. * of the instruction that can cause the exception and
  136. * not the instruction itself.
  137. */
  138. TRACE_ALT_INFO_NOADDR(orig_insn, "/ ", "%s for instruction at 0x%lx <%s+0x%lx>",
  139. alt_name,
  140. orig_insn->offset, orig_insn->sym->name,
  141. orig_insn->offset - orig_insn->sym->offset);
  142. } else {
  143. TRACE_ALT_INFO_NOADDR(orig_insn, "/ ", "%s", alt_name);
  144. }
  145. if (alt->type == ALT_TYPE_JUMP_TABLE) {
  146. /*
  147. * For a jump alternative, if the default instruction is
  148. * a NOP then it is replaced with the jmp instruction,
  149. * otherwise it is replaced with a NOP instruction.
  150. */
  151. trace_depth++;
  152. if (orig_insn->type == INSN_NOP) {
  153. suffix[0] = (orig_insn->len == 5) ? 'q' : '\0';
  154. TRACE_ADDR(orig_insn, "jmp%-3s %lx <%s+0x%lx>", suffix,
  155. alt_insn->offset, alt_insn->sym->name,
  156. alt_insn->offset - alt_insn->sym->offset);
  157. } else {
  158. TRACE_ADDR(orig_insn, "nop%d", orig_insn->len);
  159. trace_depth--;
  160. }
  161. }
  162. }
  163. void trace_alt_end(struct instruction *orig_insn, struct alternative *alt,
  164. char *alt_name)
  165. {
  166. if (alt->type == ALT_TYPE_JUMP_TABLE && orig_insn->type == INSN_NOP)
  167. trace_depth--;
  168. TRACE_ALT_INFO_NOADDR(orig_insn, "\\ ", "%s", alt_name);
  169. }