xlated_dumper.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
  2. /* Copyright (C) 2018 Netronome Systems, Inc. */
  3. #ifndef _GNU_SOURCE
  4. #define _GNU_SOURCE
  5. #endif
  6. #include <stdarg.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <sys/types.h>
  11. #include <bpf/libbpf.h>
  12. #include <bpf/libbpf_internal.h>
  13. #include "disasm.h"
  14. #include "json_writer.h"
  15. #include "main.h"
  16. #include "xlated_dumper.h"
  17. static int kernel_syms_cmp(const void *sym_a, const void *sym_b)
  18. {
  19. return ((struct kernel_sym *)sym_a)->address -
  20. ((struct kernel_sym *)sym_b)->address;
  21. }
  22. void kernel_syms_load(struct dump_data *dd)
  23. {
  24. struct kernel_sym *sym;
  25. char buff[256];
  26. void *tmp, *address;
  27. FILE *fp;
  28. fp = fopen("/proc/kallsyms", "r");
  29. if (!fp)
  30. return;
  31. while (fgets(buff, sizeof(buff), fp)) {
  32. tmp = libbpf_reallocarray(dd->sym_mapping, dd->sym_count + 1,
  33. sizeof(*dd->sym_mapping));
  34. if (!tmp) {
  35. out:
  36. free(dd->sym_mapping);
  37. dd->sym_mapping = NULL;
  38. fclose(fp);
  39. return;
  40. }
  41. dd->sym_mapping = tmp;
  42. sym = &dd->sym_mapping[dd->sym_count];
  43. /* module is optional */
  44. sym->module[0] = '\0';
  45. /* trim the square brackets around the module name */
  46. if (sscanf(buff, "%p %*c %s [%[^]]s", &address, sym->name, sym->module) < 2)
  47. continue;
  48. sym->address = (unsigned long)address;
  49. if (!strcmp(sym->name, "__bpf_call_base")) {
  50. dd->address_call_base = sym->address;
  51. /* sysctl kernel.kptr_restrict was set */
  52. if (!sym->address)
  53. goto out;
  54. }
  55. if (sym->address)
  56. dd->sym_count++;
  57. }
  58. fclose(fp);
  59. qsort(dd->sym_mapping, dd->sym_count,
  60. sizeof(*dd->sym_mapping), kernel_syms_cmp);
  61. }
  62. void kernel_syms_destroy(struct dump_data *dd)
  63. {
  64. free(dd->sym_mapping);
  65. }
  66. struct kernel_sym *kernel_syms_search(struct dump_data *dd,
  67. unsigned long key)
  68. {
  69. struct kernel_sym sym = {
  70. .address = key,
  71. };
  72. return dd->sym_mapping ?
  73. bsearch(&sym, dd->sym_mapping, dd->sym_count,
  74. sizeof(*dd->sym_mapping), kernel_syms_cmp) : NULL;
  75. }
  76. static void __printf(2, 3) print_insn(void *private_data, const char *fmt, ...)
  77. {
  78. va_list args;
  79. va_start(args, fmt);
  80. vprintf(fmt, args);
  81. va_end(args);
  82. }
  83. static void __printf(2, 3)
  84. print_insn_for_graph(void *private_data, const char *fmt, ...)
  85. {
  86. char buf[64], *p;
  87. va_list args;
  88. va_start(args, fmt);
  89. vsnprintf(buf, sizeof(buf), fmt, args);
  90. va_end(args);
  91. p = buf;
  92. while (*p != '\0') {
  93. if (*p == '\n') {
  94. memmove(p + 3, p, strlen(buf) + 1 - (p - buf));
  95. /* Align each instruction dump row left. */
  96. *p++ = '\\';
  97. *p++ = 'l';
  98. /* Output multiline concatenation. */
  99. *p++ = '\\';
  100. } else if (*p == '<' || *p == '>' || *p == '|' || *p == '&') {
  101. memmove(p + 1, p, strlen(buf) + 1 - (p - buf));
  102. /* Escape special character. */
  103. *p++ = '\\';
  104. }
  105. p++;
  106. }
  107. printf("%s", buf);
  108. }
  109. static void __printf(2, 3)
  110. print_insn_json(void *private_data, const char *fmt, ...)
  111. {
  112. unsigned int l = strlen(fmt);
  113. char chomped_fmt[l];
  114. va_list args;
  115. va_start(args, fmt);
  116. if (l > 0) {
  117. strncpy(chomped_fmt, fmt, l - 1);
  118. chomped_fmt[l - 1] = '\0';
  119. }
  120. jsonw_vprintf_enquote(json_wtr, chomped_fmt, args);
  121. va_end(args);
  122. }
  123. static const char *print_call_pcrel(struct dump_data *dd,
  124. struct kernel_sym *sym,
  125. unsigned long address,
  126. const struct bpf_insn *insn)
  127. {
  128. if (!dd->nr_jited_ksyms)
  129. /* Do not show address for interpreted programs */
  130. snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
  131. "%+d", insn->off);
  132. else if (sym)
  133. snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
  134. "%+d#%s", insn->off, sym->name);
  135. else
  136. snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
  137. "%+d#0x%lx", insn->off, address);
  138. return dd->scratch_buff;
  139. }
  140. static const char *print_call_helper(struct dump_data *dd,
  141. struct kernel_sym *sym,
  142. unsigned long address)
  143. {
  144. if (sym)
  145. snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
  146. "%s", sym->name);
  147. else
  148. snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
  149. "0x%lx", address);
  150. return dd->scratch_buff;
  151. }
  152. static const char *print_call(void *private_data,
  153. const struct bpf_insn *insn)
  154. {
  155. struct dump_data *dd = private_data;
  156. unsigned long address = dd->address_call_base + insn->imm;
  157. struct kernel_sym *sym;
  158. if (insn->src_reg == BPF_PSEUDO_CALL &&
  159. (__u32) insn->imm < dd->nr_jited_ksyms && dd->jited_ksyms)
  160. address = dd->jited_ksyms[insn->imm];
  161. sym = kernel_syms_search(dd, address);
  162. if (insn->src_reg == BPF_PSEUDO_CALL)
  163. return print_call_pcrel(dd, sym, address, insn);
  164. else
  165. return print_call_helper(dd, sym, address);
  166. }
  167. static const char *print_imm(void *private_data,
  168. const struct bpf_insn *insn,
  169. __u64 full_imm)
  170. {
  171. struct dump_data *dd = private_data;
  172. if (insn->src_reg == BPF_PSEUDO_MAP_FD)
  173. snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
  174. "map[id:%d]", insn->imm);
  175. else if (insn->src_reg == BPF_PSEUDO_MAP_VALUE)
  176. snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
  177. "map[id:%d][0]+%d", insn->imm, (insn + 1)->imm);
  178. else if (insn->src_reg == BPF_PSEUDO_MAP_IDX_VALUE)
  179. snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
  180. "map[idx:%d]+%d", insn->imm, (insn + 1)->imm);
  181. else if (insn->src_reg == BPF_PSEUDO_FUNC)
  182. snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
  183. "subprog[%+d]", insn->imm);
  184. else
  185. snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
  186. "0x%llx", (unsigned long long)full_imm);
  187. return dd->scratch_buff;
  188. }
  189. void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
  190. bool opcodes, bool linum)
  191. {
  192. const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
  193. const struct bpf_insn_cbs cbs = {
  194. .cb_print = print_insn_json,
  195. .cb_call = print_call,
  196. .cb_imm = print_imm,
  197. .private_data = dd,
  198. };
  199. struct bpf_func_info *record;
  200. struct bpf_insn *insn = buf;
  201. struct btf *btf = dd->btf;
  202. bool double_insn = false;
  203. unsigned int nr_skip = 0;
  204. char func_sig[1024];
  205. unsigned int i;
  206. jsonw_start_array(json_wtr);
  207. record = dd->func_info;
  208. for (i = 0; i < len / sizeof(*insn); i++) {
  209. if (double_insn) {
  210. double_insn = false;
  211. continue;
  212. }
  213. double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
  214. jsonw_start_object(json_wtr);
  215. if (btf && record) {
  216. if (record->insn_off == i) {
  217. btf_dumper_type_only(btf, record->type_id,
  218. func_sig,
  219. sizeof(func_sig));
  220. if (func_sig[0] != '\0') {
  221. jsonw_name(json_wtr, "proto");
  222. jsonw_string(json_wtr, func_sig);
  223. }
  224. record = (void *)record + dd->finfo_rec_size;
  225. }
  226. }
  227. if (prog_linfo) {
  228. const struct bpf_line_info *linfo;
  229. linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
  230. if (linfo) {
  231. btf_dump_linfo_json(btf, linfo, linum);
  232. nr_skip++;
  233. }
  234. }
  235. jsonw_name(json_wtr, "disasm");
  236. print_bpf_insn(&cbs, insn + i, true);
  237. if (opcodes) {
  238. jsonw_name(json_wtr, "opcodes");
  239. jsonw_start_object(json_wtr);
  240. jsonw_name(json_wtr, "code");
  241. jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code);
  242. jsonw_name(json_wtr, "src_reg");
  243. jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg);
  244. jsonw_name(json_wtr, "dst_reg");
  245. jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg);
  246. jsonw_name(json_wtr, "off");
  247. print_hex_data_json((uint8_t *)(&insn[i].off), 2);
  248. jsonw_name(json_wtr, "imm");
  249. if (double_insn && i < len - 1)
  250. print_hex_data_json((uint8_t *)(&insn[i].imm),
  251. 12);
  252. else
  253. print_hex_data_json((uint8_t *)(&insn[i].imm),
  254. 4);
  255. jsonw_end_object(json_wtr);
  256. }
  257. jsonw_end_object(json_wtr);
  258. }
  259. jsonw_end_array(json_wtr);
  260. }
  261. void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
  262. bool opcodes, bool linum)
  263. {
  264. const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
  265. const struct bpf_insn_cbs cbs = {
  266. .cb_print = print_insn,
  267. .cb_call = print_call,
  268. .cb_imm = print_imm,
  269. .private_data = dd,
  270. };
  271. struct bpf_func_info *record;
  272. struct bpf_insn *insn = buf;
  273. struct btf *btf = dd->btf;
  274. unsigned int nr_skip = 0;
  275. bool double_insn = false;
  276. char func_sig[1024];
  277. unsigned int i;
  278. record = dd->func_info;
  279. for (i = 0; i < len / sizeof(*insn); i++) {
  280. if (double_insn) {
  281. double_insn = false;
  282. continue;
  283. }
  284. if (btf && record) {
  285. if (record->insn_off == i) {
  286. btf_dumper_type_only(btf, record->type_id,
  287. func_sig,
  288. sizeof(func_sig));
  289. if (func_sig[0] != '\0')
  290. printf("%s:\n", func_sig);
  291. record = (void *)record + dd->finfo_rec_size;
  292. }
  293. }
  294. if (prog_linfo) {
  295. const struct bpf_line_info *linfo;
  296. linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
  297. if (linfo) {
  298. btf_dump_linfo_plain(btf, linfo, "; ",
  299. linum);
  300. nr_skip++;
  301. }
  302. }
  303. double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
  304. printf("%4u: ", i);
  305. print_bpf_insn(&cbs, insn + i, true);
  306. if (opcodes) {
  307. printf(" ");
  308. fprint_hex(stdout, insn + i, 8, " ");
  309. if (double_insn && i < len - 1) {
  310. printf(" ");
  311. fprint_hex(stdout, insn + i + 1, 8, " ");
  312. }
  313. printf("\n");
  314. }
  315. }
  316. }
  317. void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
  318. unsigned int start_idx,
  319. bool opcodes, bool linum)
  320. {
  321. const struct bpf_insn_cbs cbs = {
  322. .cb_print = print_insn_for_graph,
  323. .cb_call = print_call,
  324. .cb_imm = print_imm,
  325. .private_data = dd,
  326. };
  327. const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
  328. const struct bpf_line_info *last_linfo = NULL;
  329. struct bpf_func_info *record = dd->func_info;
  330. struct bpf_insn *insn_start = buf_start;
  331. struct bpf_insn *insn_end = buf_end;
  332. struct bpf_insn *cur = insn_start;
  333. struct btf *btf = dd->btf;
  334. bool double_insn = false;
  335. char func_sig[1024];
  336. for (; cur <= insn_end; cur++) {
  337. unsigned int insn_off;
  338. if (double_insn) {
  339. double_insn = false;
  340. continue;
  341. }
  342. double_insn = cur->code == (BPF_LD | BPF_IMM | BPF_DW);
  343. insn_off = (unsigned int)(cur - insn_start + start_idx);
  344. if (btf && record) {
  345. if (record->insn_off == insn_off) {
  346. btf_dumper_type_only(btf, record->type_id,
  347. func_sig,
  348. sizeof(func_sig));
  349. if (func_sig[0] != '\0')
  350. printf("; %s:\\l\\\n", func_sig);
  351. record = (void *)record + dd->finfo_rec_size;
  352. }
  353. }
  354. if (prog_linfo) {
  355. const struct bpf_line_info *linfo;
  356. linfo = bpf_prog_linfo__lfind(prog_linfo, insn_off, 0);
  357. if (linfo && linfo != last_linfo) {
  358. btf_dump_linfo_dotlabel(btf, linfo, linum);
  359. last_linfo = linfo;
  360. }
  361. }
  362. printf("%u: ", insn_off);
  363. print_bpf_insn(&cbs, cur, true);
  364. if (opcodes) {
  365. printf("\\ \\ \\ \\ ");
  366. fprint_hex(stdout, cur, 8, " ");
  367. if (double_insn && cur <= insn_end - 1) {
  368. printf(" ");
  369. fprint_hex(stdout, cur + 1, 8, " ");
  370. }
  371. printf("\\l\\\n");
  372. }
  373. if (cur != insn_end)
  374. printf("| ");
  375. }
  376. }