orc_gen.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (C) 2017 Josh Poimboeuf <[email protected]>
  4. */
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <linux/objtool.h>
  8. #include <asm/orc_types.h>
  9. #include <objtool/check.h>
  10. #include <objtool/warn.h>
  11. #include <objtool/endianness.h>
  12. static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi,
  13. struct instruction *insn)
  14. {
  15. struct cfi_reg *bp = &cfi->regs[CFI_BP];
  16. memset(orc, 0, sizeof(*orc));
  17. if (!cfi) {
  18. orc->end = 0;
  19. orc->sp_reg = ORC_REG_UNDEFINED;
  20. return 0;
  21. }
  22. orc->end = cfi->end;
  23. if (cfi->cfa.base == CFI_UNDEFINED) {
  24. orc->sp_reg = ORC_REG_UNDEFINED;
  25. return 0;
  26. }
  27. switch (cfi->cfa.base) {
  28. case CFI_SP:
  29. orc->sp_reg = ORC_REG_SP;
  30. break;
  31. case CFI_SP_INDIRECT:
  32. orc->sp_reg = ORC_REG_SP_INDIRECT;
  33. break;
  34. case CFI_BP:
  35. orc->sp_reg = ORC_REG_BP;
  36. break;
  37. case CFI_BP_INDIRECT:
  38. orc->sp_reg = ORC_REG_BP_INDIRECT;
  39. break;
  40. case CFI_R10:
  41. orc->sp_reg = ORC_REG_R10;
  42. break;
  43. case CFI_R13:
  44. orc->sp_reg = ORC_REG_R13;
  45. break;
  46. case CFI_DI:
  47. orc->sp_reg = ORC_REG_DI;
  48. break;
  49. case CFI_DX:
  50. orc->sp_reg = ORC_REG_DX;
  51. break;
  52. default:
  53. WARN_FUNC("unknown CFA base reg %d",
  54. insn->sec, insn->offset, cfi->cfa.base);
  55. return -1;
  56. }
  57. switch (bp->base) {
  58. case CFI_UNDEFINED:
  59. orc->bp_reg = ORC_REG_UNDEFINED;
  60. break;
  61. case CFI_CFA:
  62. orc->bp_reg = ORC_REG_PREV_SP;
  63. break;
  64. case CFI_BP:
  65. orc->bp_reg = ORC_REG_BP;
  66. break;
  67. default:
  68. WARN_FUNC("unknown BP base reg %d",
  69. insn->sec, insn->offset, bp->base);
  70. return -1;
  71. }
  72. orc->sp_offset = cfi->cfa.offset;
  73. orc->bp_offset = bp->offset;
  74. orc->type = cfi->type;
  75. return 0;
  76. }
  77. static int write_orc_entry(struct elf *elf, struct section *orc_sec,
  78. struct section *ip_sec, unsigned int idx,
  79. struct section *insn_sec, unsigned long insn_off,
  80. struct orc_entry *o)
  81. {
  82. struct orc_entry *orc;
  83. /* populate ORC data */
  84. orc = (struct orc_entry *)orc_sec->data->d_buf + idx;
  85. memcpy(orc, o, sizeof(*orc));
  86. orc->sp_offset = bswap_if_needed(orc->sp_offset);
  87. orc->bp_offset = bswap_if_needed(orc->bp_offset);
  88. /* populate reloc for ip */
  89. if (elf_add_reloc_to_insn(elf, ip_sec, idx * sizeof(int), R_X86_64_PC32,
  90. insn_sec, insn_off))
  91. return -1;
  92. return 0;
  93. }
  94. struct orc_list_entry {
  95. struct list_head list;
  96. struct orc_entry orc;
  97. struct section *insn_sec;
  98. unsigned long insn_off;
  99. };
  100. static int orc_list_add(struct list_head *orc_list, struct orc_entry *orc,
  101. struct section *sec, unsigned long offset)
  102. {
  103. struct orc_list_entry *entry = malloc(sizeof(*entry));
  104. if (!entry) {
  105. WARN("malloc failed");
  106. return -1;
  107. }
  108. entry->orc = *orc;
  109. entry->insn_sec = sec;
  110. entry->insn_off = offset;
  111. list_add_tail(&entry->list, orc_list);
  112. return 0;
  113. }
  114. static unsigned long alt_group_len(struct alt_group *alt_group)
  115. {
  116. return alt_group->last_insn->offset +
  117. alt_group->last_insn->len -
  118. alt_group->first_insn->offset;
  119. }
  120. int orc_create(struct objtool_file *file)
  121. {
  122. struct section *sec, *orc_sec;
  123. unsigned int nr = 0, idx = 0;
  124. struct orc_list_entry *entry;
  125. struct list_head orc_list;
  126. struct orc_entry null = {
  127. .sp_reg = ORC_REG_UNDEFINED,
  128. .bp_reg = ORC_REG_UNDEFINED,
  129. .type = UNWIND_HINT_TYPE_CALL,
  130. };
  131. /* Build a deduplicated list of ORC entries: */
  132. INIT_LIST_HEAD(&orc_list);
  133. for_each_sec(file, sec) {
  134. struct orc_entry orc, prev_orc = {0};
  135. struct instruction *insn;
  136. bool empty = true;
  137. if (!sec->text)
  138. continue;
  139. sec_for_each_insn(file, sec, insn) {
  140. struct alt_group *alt_group = insn->alt_group;
  141. int i;
  142. if (!alt_group) {
  143. if (init_orc_entry(&orc, insn->cfi, insn))
  144. return -1;
  145. if (!memcmp(&prev_orc, &orc, sizeof(orc)))
  146. continue;
  147. if (orc_list_add(&orc_list, &orc, sec,
  148. insn->offset))
  149. return -1;
  150. nr++;
  151. prev_orc = orc;
  152. empty = false;
  153. continue;
  154. }
  155. /*
  156. * Alternatives can have different stack layout
  157. * possibilities (but they shouldn't conflict).
  158. * Instead of traversing the instructions, use the
  159. * alt_group's flattened byte-offset-addressed CFI
  160. * array.
  161. */
  162. for (i = 0; i < alt_group_len(alt_group); i++) {
  163. struct cfi_state *cfi = alt_group->cfi[i];
  164. if (!cfi)
  165. continue;
  166. /* errors are reported on the original insn */
  167. if (init_orc_entry(&orc, cfi, insn))
  168. return -1;
  169. if (!memcmp(&prev_orc, &orc, sizeof(orc)))
  170. continue;
  171. if (orc_list_add(&orc_list, &orc, insn->sec,
  172. insn->offset + i))
  173. return -1;
  174. nr++;
  175. prev_orc = orc;
  176. empty = false;
  177. }
  178. /* Skip to the end of the alt_group */
  179. insn = alt_group->last_insn;
  180. }
  181. /* Add a section terminator */
  182. if (!empty) {
  183. orc_list_add(&orc_list, &null, sec, sec->sh.sh_size);
  184. nr++;
  185. }
  186. }
  187. if (!nr)
  188. return 0;
  189. /* Create .orc_unwind, .orc_unwind_ip and .rela.orc_unwind_ip sections: */
  190. sec = find_section_by_name(file->elf, ".orc_unwind");
  191. if (sec) {
  192. WARN("file already has .orc_unwind section, skipping");
  193. return -1;
  194. }
  195. orc_sec = elf_create_section(file->elf, ".orc_unwind", 0,
  196. sizeof(struct orc_entry), nr);
  197. if (!orc_sec)
  198. return -1;
  199. sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), nr);
  200. if (!sec)
  201. return -1;
  202. /* Write ORC entries to sections: */
  203. list_for_each_entry(entry, &orc_list, list) {
  204. if (write_orc_entry(file->elf, orc_sec, sec, idx++,
  205. entry->insn_sec, entry->insn_off,
  206. &entry->orc))
  207. return -1;
  208. }
  209. return 0;
  210. }