orc_dump.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (C) 2017 Josh Poimboeuf <[email protected]>
  4. */
  5. #include <unistd.h>
  6. #include <linux/objtool.h>
  7. #include <asm/orc_types.h>
  8. #include <objtool/objtool.h>
  9. #include <objtool/warn.h>
  10. #include <objtool/endianness.h>
  11. static const char *reg_name(unsigned int reg)
  12. {
  13. switch (reg) {
  14. case ORC_REG_PREV_SP:
  15. return "prevsp";
  16. case ORC_REG_DX:
  17. return "dx";
  18. case ORC_REG_DI:
  19. return "di";
  20. case ORC_REG_BP:
  21. return "bp";
  22. case ORC_REG_SP:
  23. return "sp";
  24. case ORC_REG_R10:
  25. return "r10";
  26. case ORC_REG_R13:
  27. return "r13";
  28. case ORC_REG_BP_INDIRECT:
  29. return "bp(ind)";
  30. case ORC_REG_SP_INDIRECT:
  31. return "sp(ind)";
  32. default:
  33. return "?";
  34. }
  35. }
  36. static const char *orc_type_name(unsigned int type)
  37. {
  38. switch (type) {
  39. case UNWIND_HINT_TYPE_CALL:
  40. return "call";
  41. case UNWIND_HINT_TYPE_REGS:
  42. return "regs";
  43. case UNWIND_HINT_TYPE_REGS_PARTIAL:
  44. return "regs (partial)";
  45. default:
  46. return "?";
  47. }
  48. }
  49. static void print_reg(unsigned int reg, int offset)
  50. {
  51. if (reg == ORC_REG_BP_INDIRECT)
  52. printf("(bp%+d)", offset);
  53. else if (reg == ORC_REG_SP_INDIRECT)
  54. printf("(sp)%+d", offset);
  55. else if (reg == ORC_REG_UNDEFINED)
  56. printf("(und)");
  57. else
  58. printf("%s%+d", reg_name(reg), offset);
  59. }
  60. int orc_dump(const char *_objname)
  61. {
  62. int fd, nr_entries, i, *orc_ip = NULL, orc_size = 0;
  63. struct orc_entry *orc = NULL;
  64. char *name;
  65. size_t nr_sections;
  66. Elf64_Addr orc_ip_addr = 0;
  67. size_t shstrtab_idx, strtab_idx = 0;
  68. Elf *elf;
  69. Elf_Scn *scn;
  70. GElf_Shdr sh;
  71. GElf_Rela rela;
  72. GElf_Sym sym;
  73. Elf_Data *data, *symtab = NULL, *rela_orc_ip = NULL;
  74. objname = _objname;
  75. elf_version(EV_CURRENT);
  76. fd = open(objname, O_RDONLY);
  77. if (fd == -1) {
  78. perror("open");
  79. return -1;
  80. }
  81. elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
  82. if (!elf) {
  83. WARN_ELF("elf_begin");
  84. return -1;
  85. }
  86. if (elf_getshdrnum(elf, &nr_sections)) {
  87. WARN_ELF("elf_getshdrnum");
  88. return -1;
  89. }
  90. if (elf_getshdrstrndx(elf, &shstrtab_idx)) {
  91. WARN_ELF("elf_getshdrstrndx");
  92. return -1;
  93. }
  94. for (i = 0; i < nr_sections; i++) {
  95. scn = elf_getscn(elf, i);
  96. if (!scn) {
  97. WARN_ELF("elf_getscn");
  98. return -1;
  99. }
  100. if (!gelf_getshdr(scn, &sh)) {
  101. WARN_ELF("gelf_getshdr");
  102. return -1;
  103. }
  104. name = elf_strptr(elf, shstrtab_idx, sh.sh_name);
  105. if (!name) {
  106. WARN_ELF("elf_strptr");
  107. return -1;
  108. }
  109. data = elf_getdata(scn, NULL);
  110. if (!data) {
  111. WARN_ELF("elf_getdata");
  112. return -1;
  113. }
  114. if (!strcmp(name, ".symtab")) {
  115. symtab = data;
  116. } else if (!strcmp(name, ".strtab")) {
  117. strtab_idx = i;
  118. } else if (!strcmp(name, ".orc_unwind")) {
  119. orc = data->d_buf;
  120. orc_size = sh.sh_size;
  121. } else if (!strcmp(name, ".orc_unwind_ip")) {
  122. orc_ip = data->d_buf;
  123. orc_ip_addr = sh.sh_addr;
  124. } else if (!strcmp(name, ".rela.orc_unwind_ip")) {
  125. rela_orc_ip = data;
  126. }
  127. }
  128. if (!symtab || !strtab_idx || !orc || !orc_ip)
  129. return 0;
  130. if (orc_size % sizeof(*orc) != 0) {
  131. WARN("bad .orc_unwind section size");
  132. return -1;
  133. }
  134. nr_entries = orc_size / sizeof(*orc);
  135. for (i = 0; i < nr_entries; i++) {
  136. if (rela_orc_ip) {
  137. if (!gelf_getrela(rela_orc_ip, i, &rela)) {
  138. WARN_ELF("gelf_getrela");
  139. return -1;
  140. }
  141. if (!gelf_getsym(symtab, GELF_R_SYM(rela.r_info), &sym)) {
  142. WARN_ELF("gelf_getsym");
  143. return -1;
  144. }
  145. if (GELF_ST_TYPE(sym.st_info) == STT_SECTION) {
  146. scn = elf_getscn(elf, sym.st_shndx);
  147. if (!scn) {
  148. WARN_ELF("elf_getscn");
  149. return -1;
  150. }
  151. if (!gelf_getshdr(scn, &sh)) {
  152. WARN_ELF("gelf_getshdr");
  153. return -1;
  154. }
  155. name = elf_strptr(elf, shstrtab_idx, sh.sh_name);
  156. if (!name) {
  157. WARN_ELF("elf_strptr");
  158. return -1;
  159. }
  160. } else {
  161. name = elf_strptr(elf, strtab_idx, sym.st_name);
  162. if (!name) {
  163. WARN_ELF("elf_strptr");
  164. return -1;
  165. }
  166. }
  167. printf("%s+%llx:", name, (unsigned long long)rela.r_addend);
  168. } else {
  169. printf("%llx:", (unsigned long long)(orc_ip_addr + (i * sizeof(int)) + orc_ip[i]));
  170. }
  171. printf(" sp:");
  172. print_reg(orc[i].sp_reg, bswap_if_needed(orc[i].sp_offset));
  173. printf(" bp:");
  174. print_reg(orc[i].bp_reg, bswap_if_needed(orc[i].bp_offset));
  175. printf(" type:%s end:%d\n",
  176. orc_type_name(orc[i].type), orc[i].end);
  177. }
  178. elf_end(elf);
  179. close(fd);
  180. return 0;
  181. }