main.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. /*
  2. * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. /**
  7. * @file DWARF Exception Frames parser header
  8. *
  9. * This file describes the frame types for x86, required for
  10. * parsing `eh_frame` and `eh_frame_hdr`.
  11. */
  12. #define _POSIX_C_SOURCE 200809L
  13. #define _DEFAULT_SOURCE
  14. #include <stdio.h>
  15. #include <signal.h>
  16. #include <string.h>
  17. #include <unistd.h>
  18. #include <stdlib.h>
  19. #include <stdbool.h>
  20. #include <assert.h>
  21. #include <ucontext.h>
  22. #include "../include/esp_private/eh_frame_parser.h"
  23. #include "eh_frame_parser_impl.h"
  24. /**
  25. * @brief Index of x86 registers in `greg_t` structure.
  26. */
  27. #define REG_EDI 4
  28. #define REG_ESI 5
  29. #define REG_EBP 6
  30. #define REG_ESP 7
  31. #define REG_EBX 8
  32. #define REG_EDX 9
  33. #define REG_ECX 10
  34. #define REG_EAX 11
  35. #define REG_EIP 14
  36. /**
  37. * @brief Number of functions in the funs structure described below.
  38. */
  39. #define FUNCTIONS_COUNT ((sizeof(funs)/sizeof(*funs)))
  40. /**
  41. * @brief Number which will determine the depth of the call stack.
  42. * Check `main()` for more information.
  43. */
  44. #define NUMBER_TO_TEST (4)
  45. /**
  46. * @brief Number of iteration for function `esp_eh_frame_generated_step`.
  47. */
  48. #define NUMBER_OF_ITERATION (2 * NUMBER_TO_TEST + 2 + 1)
  49. /**
  50. * @brief Define a simple linked list type and initialize one.
  51. */
  52. struct list_t {
  53. uint32_t value;
  54. struct list_t *next;
  55. };
  56. static struct list_t head = { 0 };
  57. /**
  58. * Few recursive functions to make the the call stack a bit more complex than a
  59. * single function call would give.
  60. */
  61. bool is_odd(uint32_t n);
  62. bool is_even(uint32_t n);
  63. void browse_list(struct list_t* l);
  64. /**
  65. * @brief Structure defining a function of our program.
  66. * This will be used to translate the backtrace.
  67. */
  68. struct functions_info {
  69. const char* name;
  70. uintptr_t start;
  71. uintptr_t end; /* will be filled at runtime */
  72. };
  73. /**
  74. * @brief Structure storing the information about the
  75. * function that will be part of the backtrace.
  76. */
  77. struct functions_info funs[] = {
  78. {
  79. .name = "browse_list",
  80. .start = (uintptr_t) &browse_list,
  81. .end = 0
  82. },
  83. {
  84. .name = "is_odd",
  85. .start = (uintptr_t) &is_odd,
  86. .end = 0
  87. },
  88. {
  89. .name = "is_even",
  90. .start = (uintptr_t) &is_even,
  91. .end = 0
  92. }
  93. };
  94. /**
  95. * @brief Test whether the address passed as PC is part of the function which
  96. * name is `function_name`. The global array `funs` is used.
  97. *
  98. * @param pc Program counter to test. (address in the program)
  99. * @param function_name Function name to check the address of.
  100. *
  101. * @return true if PC is in the function called `function_name`, false else.
  102. */
  103. bool is_pc_in_function(const uint32_t pc, const char* function_name)
  104. {
  105. for (uint32_t i = 0; i < FUNCTIONS_COUNT; i++) {
  106. const struct functions_info current_fun = funs[i];
  107. if (strcmp(current_fun.name, function_name) == 0) {
  108. return current_fun.start <= pc && pc <= current_fun.end;
  109. }
  110. }
  111. /* Function not found. */
  112. return false;
  113. }
  114. /**
  115. * @brief Number of times `esp_eh_frame_generated_step` is called.
  116. */
  117. static uint32_t iteration = 1;
  118. /**
  119. * @brief Override the default function called when a backtrace step is
  120. * generated.
  121. */
  122. void esp_eh_frame_generated_step(uint32_t pc, uint32_t sp) {
  123. /* The first PCs in the backtrace are calls to `browse_list()` + 2.
  124. * This is due to the fact that the list contains all the numbers
  125. * between NUMBER_TO_TEST to 0 included. Moreover, another call
  126. * is made when we meet the NULL pointer. */
  127. if (iteration > 0 && iteration <= (NUMBER_TO_TEST + 2)) {
  128. assert(is_pc_in_function(pc, "browse_list"));
  129. } else {
  130. /**
  131. * The backtrace should be:
  132. * - in is_odd when iteration is even.
  133. * - in is_even when iteration is odd.
  134. *
  135. * The backtrace finishes when the iteration reaches the end of
  136. * browse_list (NUMBER_TO_TEST + 2 iterations), is_even
  137. * (NUMBER_TO_TEST/2 calls) and is_odd (NUMBER_TO_TEST/2 calls) calls.
  138. */
  139. if (iteration >= NUMBER_OF_ITERATION)
  140. return;
  141. else if (iteration % 2 == 0)
  142. assert(is_pc_in_function(pc, "is_odd"));
  143. else
  144. assert(is_pc_in_function(pc, "is_even"));
  145. }
  146. /* Number of times this function has been entered. */
  147. iteration++;
  148. }
  149. /**
  150. * @brief Handler called when SIGSEV signal is sent to the program.
  151. *
  152. * @param signal Signal received byt the program. Shall be SIGSEGV.
  153. * @param info Structure containing info about the error itself. Ignored.
  154. * @param ucontext Context of the program when the error occurred. This
  155. * is used to retrieve the CPU registers value.
  156. */
  157. void signal_handler(int signal, siginfo_t *info, void *ucontext) {
  158. /* Setup the execution frame as expected by the eh_frame_parser.
  159. * Indeed, the registers index defined in ucontext.h are NOT the same
  160. * the registers index DWARF is expecting. */
  161. ucontext_t* context = (ucontext_t*) ucontext;
  162. greg_t *gregset = context->uc_mcontext.gregs;
  163. x86ExcFrame frame = {
  164. .eax = gregset[REG_EAX],
  165. .ecx = gregset[REG_ECX],
  166. .edx = gregset[REG_EDX],
  167. .ebx = gregset[REG_EBX],
  168. .esp = gregset[REG_ESP],
  169. .ebp = gregset[REG_EBP],
  170. .esi = gregset[REG_ESI],
  171. .edi = gregset[REG_EDI],
  172. .eip = gregset[REG_EIP]
  173. };
  174. /* The following function will use panic_print_str and panic_print_hex
  175. * function to output the data.
  176. * Instead of replacing stdout file descriptor with a pipe, we can simply
  177. * replace these functions to store the data instead of printing them.
  178. */
  179. esp_eh_frame_print_backtrace(&frame);
  180. /* No assert has been triggered, the backtrace succeeded if the number of
  181. * iterations of function `esp_eh_frame_generated_step` is correct. */
  182. if (iteration == NUMBER_OF_ITERATION) {
  183. printf("\e[32m\e[1mAll tests passed \e[0m\r\n");
  184. } else {
  185. printf("\e[31m\e[1mWrong length of backtrace (%d iteration, expected %d) \e[0m\r\n",
  186. iteration, NUMBER_OF_ITERATION);
  187. exit(1);
  188. }
  189. /* Everything went fine, exit normally. */
  190. exit(0);
  191. }
  192. /**
  193. * @brief Browse the list passed as an argument.
  194. * The following function will trigger a SIGSEV signal on purpose, in order to
  195. * generate the backtrace.
  196. *
  197. * @param l List to browse.
  198. */
  199. void browse_list(struct list_t* l) {
  200. browse_list(l->next);
  201. }
  202. /**
  203. * @brief Add a number to the global list `head`.
  204. *
  205. * @param n Number to add in the list.
  206. */
  207. void add_number_to_list(uint32_t n) {
  208. struct list_t* l = malloc(sizeof(struct list_t));
  209. l->value = n;
  210. l->next = head.next;
  211. head.next = l;
  212. }
  213. /**
  214. * @brief Test if the number passed is even.
  215. * This function will fail, on purpose.
  216. *
  217. * @param n Number to test.
  218. *
  219. * @return true if even, false else.
  220. */
  221. bool is_even(uint32_t n) {
  222. add_number_to_list(n);
  223. if (n == 0) {
  224. browse_list(head.next);
  225. return true;
  226. }
  227. return is_odd(n - 1);
  228. }
  229. /**
  230. * @brief Test if the number passed is odd.
  231. * This function will fail, on purpose.
  232. *
  233. * @param n Number to test.
  234. *
  235. * @return true if odd, false else.
  236. */
  237. bool is_odd(uint32_t n) {
  238. add_number_to_list(n);
  239. if (n == 0) {
  240. browse_list(head.next);
  241. return false;
  242. }
  243. return is_even(n - 1);
  244. }
  245. /**
  246. * @brief Initiliaze the global `funs` array.
  247. */
  248. static inline void initialize_functions_info(void)
  249. {
  250. for (uint32_t i = 0; i < FUNCTIONS_COUNT; i++) {
  251. /* Each of the functions defined in this structure finishes
  252. * with the following instructions:
  253. * leave (0xc9)
  254. * ret (0xc3)
  255. * Thus, we will look for these instructions. */
  256. uint8_t* instructions = (uint8_t*) funs[i].start;
  257. while (instructions[0] != 0xc9 || instructions[1] != 0xc3)
  258. instructions++;
  259. instructions += 1;
  260. funs[i].end = (uintptr_t) instructions;
  261. }
  262. }
  263. /**
  264. * Call the previous functions to create a complex call stack and fail.
  265. */
  266. int main (int argc, char** argv)
  267. {
  268. /* Initialize the structure holding information about the signal to override. */
  269. struct sigaction sig = {
  270. .sa_mask = 0,
  271. .sa_flags = SA_SIGINFO,
  272. .sa_restorer = NULL,
  273. .sa_sigaction = signal_handler
  274. };
  275. /* Look for the functions end functions. */
  276. initialize_functions_info();
  277. /* Override default SIGSEV signal callback. */
  278. int res = sigaction(SIGSEGV, &sig, NULL);
  279. if (res) {
  280. perror("Could not override SIGSEV signal");
  281. return 1;
  282. }
  283. /* Trigger the segmentation fault with a complex backtrace. */
  284. is_even(NUMBER_TO_TEST);
  285. return 0;
  286. }