jit_disasm_helpers.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
  2. #include <bpf/bpf.h>
  3. #include <bpf/libbpf.h>
  4. #include <test_progs.h>
  5. #ifdef HAVE_LLVM_SUPPORT
  6. #include <llvm-c/Core.h>
  7. #include <llvm-c/Disassembler.h>
  8. #include <llvm-c/Target.h>
  9. #include <llvm-c/TargetMachine.h>
  10. /* The intent is to use get_jited_program_text() for small test
  11. * programs written in BPF assembly, thus assume that 32 local labels
  12. * would be sufficient.
  13. */
  14. #define MAX_LOCAL_LABELS 32
  15. /* Local labels are encoded as 'L42', this requires 4 bytes of storage:
  16. * 3 characters + zero byte
  17. */
  18. #define LOCAL_LABEL_LEN 4
  19. static bool llvm_initialized;
  20. struct local_labels {
  21. bool print_phase;
  22. __u32 prog_len;
  23. __u32 cnt;
  24. __u32 pcs[MAX_LOCAL_LABELS];
  25. char names[MAX_LOCAL_LABELS][LOCAL_LABEL_LEN];
  26. };
  27. static const char *lookup_symbol(void *data, uint64_t ref_value, uint64_t *ref_type,
  28. uint64_t ref_pc, const char **ref_name)
  29. {
  30. struct local_labels *labels = data;
  31. uint64_t type = *ref_type;
  32. int i;
  33. *ref_type = LLVMDisassembler_ReferenceType_InOut_None;
  34. *ref_name = NULL;
  35. if (type != LLVMDisassembler_ReferenceType_In_Branch)
  36. return NULL;
  37. /* Depending on labels->print_phase either discover local labels or
  38. * return a name assigned with local jump target:
  39. * - if print_phase is true and ref_value is in labels->pcs,
  40. * return corresponding labels->name.
  41. * - if print_phase is false, save program-local jump targets
  42. * in labels->pcs;
  43. */
  44. if (labels->print_phase) {
  45. for (i = 0; i < labels->cnt; ++i)
  46. if (labels->pcs[i] == ref_value)
  47. return labels->names[i];
  48. } else {
  49. if (labels->cnt < MAX_LOCAL_LABELS && ref_value < labels->prog_len)
  50. labels->pcs[labels->cnt++] = ref_value;
  51. }
  52. return NULL;
  53. }
  54. static int disasm_insn(LLVMDisasmContextRef ctx, uint8_t *image, __u32 len, __u32 pc,
  55. char *buf, __u32 buf_sz)
  56. {
  57. int i, cnt;
  58. cnt = LLVMDisasmInstruction(ctx, image + pc, len - pc, pc,
  59. buf, buf_sz);
  60. if (cnt > 0)
  61. return cnt;
  62. PRINT_FAIL("Can't disasm instruction at offset %d:", pc);
  63. for (i = 0; i < 16 && pc + i < len; ++i)
  64. printf(" %02x", image[pc + i]);
  65. printf("\n");
  66. return -EINVAL;
  67. }
  68. static int cmp_u32(const void *_a, const void *_b)
  69. {
  70. __u32 a = *(__u32 *)_a;
  71. __u32 b = *(__u32 *)_b;
  72. if (a < b)
  73. return -1;
  74. if (a > b)
  75. return 1;
  76. return 0;
  77. }
  78. static int disasm_one_func(FILE *text_out, uint8_t *image, __u32 len)
  79. {
  80. char *label, *colon, *triple = NULL;
  81. LLVMDisasmContextRef ctx = NULL;
  82. struct local_labels labels = {};
  83. __u32 *label_pc, pc;
  84. int i, cnt, err = 0;
  85. char buf[64];
  86. triple = LLVMGetDefaultTargetTriple();
  87. ctx = LLVMCreateDisasm(triple, &labels, 0, NULL, lookup_symbol);
  88. if (!ASSERT_OK_PTR(ctx, "LLVMCreateDisasm")) {
  89. err = -EINVAL;
  90. goto out;
  91. }
  92. cnt = LLVMSetDisasmOptions(ctx, LLVMDisassembler_Option_PrintImmHex);
  93. if (!ASSERT_EQ(cnt, 1, "LLVMSetDisasmOptions")) {
  94. err = -EINVAL;
  95. goto out;
  96. }
  97. /* discover labels */
  98. labels.prog_len = len;
  99. pc = 0;
  100. while (pc < len) {
  101. cnt = disasm_insn(ctx, image, len, pc, buf, 1);
  102. if (cnt < 0) {
  103. err = cnt;
  104. goto out;
  105. }
  106. pc += cnt;
  107. }
  108. qsort(labels.pcs, labels.cnt, sizeof(*labels.pcs), cmp_u32);
  109. for (i = 0; i < labels.cnt; ++i)
  110. /* gcc is unable to infer upper bound for labels.cnt and assumes
  111. * it to be U32_MAX. U32_MAX takes 10 decimal digits.
  112. * snprintf below prints into labels.names[*],
  113. * which has space only for two digits and a letter.
  114. * To avoid truncation warning use (i % MAX_LOCAL_LABELS),
  115. * which informs gcc about printed value upper bound.
  116. */
  117. snprintf(labels.names[i], sizeof(labels.names[i]), "L%d", i % MAX_LOCAL_LABELS);
  118. /* now print with labels */
  119. labels.print_phase = true;
  120. pc = 0;
  121. while (pc < len) {
  122. cnt = disasm_insn(ctx, image, len, pc, buf, sizeof(buf));
  123. if (cnt < 0) {
  124. err = cnt;
  125. goto out;
  126. }
  127. label_pc = bsearch(&pc, labels.pcs, labels.cnt, sizeof(*labels.pcs), cmp_u32);
  128. label = "";
  129. colon = "";
  130. if (label_pc) {
  131. label = labels.names[label_pc - labels.pcs];
  132. colon = ":";
  133. }
  134. fprintf(text_out, "%x:\t", pc);
  135. for (i = 0; i < cnt; ++i)
  136. fprintf(text_out, "%02x ", image[pc + i]);
  137. for (i = cnt * 3; i < 12 * 3; ++i)
  138. fputc(' ', text_out);
  139. fprintf(text_out, "%s%s%s\n", label, colon, buf);
  140. pc += cnt;
  141. }
  142. out:
  143. if (triple)
  144. LLVMDisposeMessage(triple);
  145. if (ctx)
  146. LLVMDisasmDispose(ctx);
  147. return err;
  148. }
  149. int get_jited_program_text(int fd, char *text, size_t text_sz)
  150. {
  151. struct bpf_prog_info info = {};
  152. __u32 info_len = sizeof(info);
  153. __u32 jited_funcs, len, pc;
  154. __u32 *func_lens = NULL;
  155. FILE *text_out = NULL;
  156. uint8_t *image = NULL;
  157. int i, err = 0;
  158. if (!llvm_initialized) {
  159. LLVMInitializeAllTargetInfos();
  160. LLVMInitializeAllTargetMCs();
  161. LLVMInitializeAllDisassemblers();
  162. llvm_initialized = 1;
  163. }
  164. text_out = fmemopen(text, text_sz, "w");
  165. if (!ASSERT_OK_PTR(text_out, "open_memstream")) {
  166. err = -errno;
  167. goto out;
  168. }
  169. /* first call is to find out jited program len */
  170. err = bpf_prog_get_info_by_fd(fd, &info, &info_len);
  171. if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd #1"))
  172. goto out;
  173. len = info.jited_prog_len;
  174. image = malloc(len);
  175. if (!ASSERT_OK_PTR(image, "malloc(info.jited_prog_len)")) {
  176. err = -ENOMEM;
  177. goto out;
  178. }
  179. jited_funcs = info.nr_jited_func_lens;
  180. func_lens = malloc(jited_funcs * sizeof(__u32));
  181. if (!ASSERT_OK_PTR(func_lens, "malloc(info.nr_jited_func_lens)")) {
  182. err = -ENOMEM;
  183. goto out;
  184. }
  185. memset(&info, 0, sizeof(info));
  186. info.jited_prog_insns = (__u64)image;
  187. info.jited_prog_len = len;
  188. info.jited_func_lens = (__u64)func_lens;
  189. info.nr_jited_func_lens = jited_funcs;
  190. err = bpf_prog_get_info_by_fd(fd, &info, &info_len);
  191. if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd #2"))
  192. goto out;
  193. for (pc = 0, i = 0; i < jited_funcs; ++i) {
  194. fprintf(text_out, "func #%d:\n", i);
  195. disasm_one_func(text_out, image + pc, func_lens[i]);
  196. fprintf(text_out, "\n");
  197. pc += func_lens[i];
  198. }
  199. out:
  200. if (text_out)
  201. fclose(text_out);
  202. if (image)
  203. free(image);
  204. if (func_lens)
  205. free(func_lens);
  206. return err;
  207. }
  208. #else /* HAVE_LLVM_SUPPORT */
  209. int get_jited_program_text(int fd, char *text, size_t text_sz)
  210. {
  211. if (env.verbosity >= VERBOSE_VERY)
  212. printf("compiled w/o llvm development libraries, can't dis-assembly binary code");
  213. return -EOPNOTSUPP;
  214. }
  215. #endif /* HAVE_LLVM_SUPPORT */