|
25 | 25 | #include <limits.h>
|
26 | 26 | #include <assert.h>
|
27 | 27 |
|
| 28 | +#include "disasm.h" |
| 29 | + |
28 | 30 | #ifndef ARRAY_SIZE
|
29 | 31 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
30 | 32 | #endif
|
@@ -181,6 +183,11 @@ struct var_preset {
|
181 | 183 | bool applied;
|
182 | 184 | };
|
183 | 185 |
|
| 186 | +struct kernel_sym { |
| 187 | + size_t address; |
| 188 | + char name[256]; |
| 189 | +}; |
| 190 | + |
184 | 191 | static struct env {
|
185 | 192 | char **filenames;
|
186 | 193 | int filename_cnt;
|
@@ -227,6 +234,7 @@ static struct env {
|
227 | 234 | char orig_cgroup[PATH_MAX];
|
228 | 235 | char stat_cgroup[PATH_MAX];
|
229 | 236 | int memory_peak_fd;
|
| 237 | + bool dump; |
230 | 238 | } env;
|
231 | 239 |
|
232 | 240 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
|
@@ -295,6 +303,7 @@ static const struct argp_option opts[] = {
|
295 | 303 | "Force BPF verifier failure on register invariant violation (BPF_F_TEST_REG_INVARIANTS program flag)" },
|
296 | 304 | { "top-src-lines", 'S', "N", 0, "Emit N most frequent source code lines" },
|
297 | 305 | { "set-global-vars", 'G', "GLOBAL", 0, "Set global variables provided in the expression, for example \"var1 = 1\"" },
|
| 306 | + { "dump", 'p', NULL, 0, "Print BPF program dump" }, |
298 | 307 | {},
|
299 | 308 | };
|
300 | 309 |
|
@@ -427,6 +436,9 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
|
427 | 436 | return err;
|
428 | 437 | }
|
429 | 438 | break;
|
| 439 | + case 'p': |
| 440 | + env.dump = true; |
| 441 | + break; |
430 | 442 | default:
|
431 | 443 | return ARGP_ERR_UNKNOWN;
|
432 | 444 | }
|
@@ -891,6 +903,14 @@ static bool is_desc_sym(char c)
|
891 | 903 | return c == 'v' || c == 'V' || c == '.' || c == '!' || c == '_';
|
892 | 904 | }
|
893 | 905 |
|
| 906 | +static const char *ltrim(const char *s) |
| 907 | +{ |
| 908 | + while (isspace(*s)) |
| 909 | + s++; |
| 910 | + |
| 911 | + return s; |
| 912 | +} |
| 913 | + |
894 | 914 | static char *rtrim(char *str)
|
895 | 915 | {
|
896 | 916 | int i;
|
@@ -1554,6 +1574,302 @@ static int parse_rvalue(const char *val, struct rvalue *rvalue)
|
1554 | 1574 | return 0;
|
1555 | 1575 | }
|
1556 | 1576 |
|
| 1577 | +static int kernel_syms_cmp(const void *sym_a, const void *sym_b) |
| 1578 | +{ |
| 1579 | + return ((struct kernel_sym *)sym_a)->address - |
| 1580 | + ((struct kernel_sym *)sym_b)->address; |
| 1581 | +} |
| 1582 | + |
| 1583 | +struct dump_context { |
| 1584 | + struct bpf_prog_info *info; |
| 1585 | + struct kernel_sym *kernel_syms; |
| 1586 | + int kernel_sym_cnt; |
| 1587 | + size_t kfunc_base_addr; |
| 1588 | + char scratch_buf[512]; |
| 1589 | +}; |
| 1590 | + |
| 1591 | +static void kernel_syms_free(struct dump_context *ctx) |
| 1592 | +{ |
| 1593 | + free(ctx->kernel_syms); |
| 1594 | + ctx->kernel_syms = NULL; |
| 1595 | + ctx->kernel_sym_cnt = 0; |
| 1596 | +} |
| 1597 | + |
| 1598 | +static void kernel_syms_load(struct dump_context *ctx) |
| 1599 | +{ |
| 1600 | + struct kernel_sym *sym; |
| 1601 | + char buff[256]; |
| 1602 | + void *tmp, *address; |
| 1603 | + FILE *fp; |
| 1604 | + |
| 1605 | + fp = fopen("/proc/kallsyms", "r"); |
| 1606 | + if (!fp) |
| 1607 | + return; |
| 1608 | + while (fgets(buff, sizeof(buff), fp)) { |
| 1609 | + tmp = reallocarray(ctx->kernel_syms, ctx->kernel_sym_cnt + 1, |
| 1610 | + sizeof(*ctx->kernel_syms)); |
| 1611 | + if (!tmp) |
| 1612 | + goto failure; |
| 1613 | + ctx->kernel_syms = tmp; |
| 1614 | + sym = ctx->kernel_syms + ctx->kernel_sym_cnt; |
| 1615 | + |
| 1616 | + if (sscanf(buff, "%p %*c %s", &address, sym->name) < 2) |
| 1617 | + continue; |
| 1618 | + sym->address = (unsigned long)address; |
| 1619 | + if (!strcmp(sym->name, "__bpf_call_base")) { |
| 1620 | + ctx->kfunc_base_addr = sym->address; |
| 1621 | + /* sysctl kernel.kptr_restrict was set */ |
| 1622 | + if (!sym->address) |
| 1623 | + goto failure; |
| 1624 | + } |
| 1625 | + if (sym->address) |
| 1626 | + ctx->kernel_sym_cnt++; |
| 1627 | + } |
| 1628 | + |
| 1629 | + fclose(fp); |
| 1630 | + qsort(ctx->kernel_syms, ctx->kernel_sym_cnt, sizeof(*ctx->kernel_syms), kernel_syms_cmp); |
| 1631 | + return; |
| 1632 | +failure: |
| 1633 | + kernel_syms_free(ctx); |
| 1634 | + fclose(fp); |
| 1635 | +} |
| 1636 | + |
| 1637 | +__attribute__((format(printf, 2, 3))) |
| 1638 | +static void print_insn(void *private_data, const char *fmt, ...) |
| 1639 | +{ |
| 1640 | + va_list args; |
| 1641 | + |
| 1642 | + va_start(args, fmt); |
| 1643 | + vprintf(fmt, args); |
| 1644 | + va_end(args); |
| 1645 | +} |
| 1646 | + |
| 1647 | +static struct kernel_sym *kernel_syms_search(unsigned long key, struct dump_context *ctx) |
| 1648 | +{ |
| 1649 | + struct kernel_sym sym = { |
| 1650 | + .address = key, |
| 1651 | + }; |
| 1652 | + |
| 1653 | + return ctx->kernel_syms ? bsearch(&sym, ctx->kernel_syms, ctx->kernel_sym_cnt, |
| 1654 | + sizeof(*ctx->kernel_syms), kernel_syms_cmp) : |
| 1655 | + NULL; |
| 1656 | +} |
| 1657 | + |
| 1658 | +static const char *print_call(void *private_data, const struct bpf_insn *insn) |
| 1659 | +{ |
| 1660 | + struct kernel_sym *sym; |
| 1661 | + struct dump_context *ctx = (struct dump_context *)private_data; |
| 1662 | + size_t address = ctx->kfunc_base_addr + insn->imm; |
| 1663 | + struct bpf_prog_info *info = ctx->info; |
| 1664 | + |
| 1665 | + if (insn->src_reg == BPF_PSEUDO_CALL) { |
| 1666 | + if ((__u32)insn->imm < info->nr_jited_ksyms && info->jited_ksyms) { |
| 1667 | + __u64 *ptr = (void *)(size_t)info->jited_ksyms; |
| 1668 | + |
| 1669 | + address = ptr[insn->imm]; |
| 1670 | + } |
| 1671 | + |
| 1672 | + sym = kernel_syms_search(address, ctx); |
| 1673 | + if (!info->jited_ksyms) |
| 1674 | + snprintf(ctx->scratch_buf, sizeof(ctx->scratch_buf), "%+d", insn->off); |
| 1675 | + else if (sym) |
| 1676 | + snprintf(ctx->scratch_buf, sizeof(ctx->scratch_buf), "%+d#%s", insn->off, |
| 1677 | + sym->name); |
| 1678 | + else |
| 1679 | + snprintf(ctx->scratch_buf, sizeof(ctx->scratch_buf), "%+d#0x%lx", insn->off, |
| 1680 | + address); |
| 1681 | + } else { |
| 1682 | + sym = kernel_syms_search(address, ctx); |
| 1683 | + if (sym) |
| 1684 | + snprintf(ctx->scratch_buf, sizeof(ctx->scratch_buf), "%s", sym->name); |
| 1685 | + else |
| 1686 | + snprintf(ctx->scratch_buf, sizeof(ctx->scratch_buf), "0x%lx", address); |
| 1687 | + } |
| 1688 | + return ctx->scratch_buf; |
| 1689 | +} |
| 1690 | + |
| 1691 | +static const char *print_imm(void *private_data, const struct bpf_insn *insn, __u64 full_imm) |
| 1692 | +{ |
| 1693 | + struct dump_context *ctx = (struct dump_context *)private_data; |
| 1694 | + |
| 1695 | + switch (insn->src_reg) { |
| 1696 | + case BPF_PSEUDO_MAP_FD: |
| 1697 | + snprintf(ctx->scratch_buf, sizeof(ctx->scratch_buf), "map[id:%d]", insn->imm); |
| 1698 | + break; |
| 1699 | + case BPF_PSEUDO_MAP_VALUE: |
| 1700 | + snprintf(ctx->scratch_buf, sizeof(ctx->scratch_buf), "map[id:%d][0]+%d", insn->imm, |
| 1701 | + insn[1].imm); |
| 1702 | + break; |
| 1703 | + case BPF_PSEUDO_MAP_IDX_VALUE: |
| 1704 | + snprintf(ctx->scratch_buf, sizeof(ctx->scratch_buf), "map[idx:%d]+%d", insn->imm, |
| 1705 | + insn[1].imm); |
| 1706 | + break; |
| 1707 | + case BPF_PSEUDO_FUNC: |
| 1708 | + snprintf(ctx->scratch_buf, sizeof(ctx->scratch_buf), "subprog[%+d]", insn->imm); |
| 1709 | + break; |
| 1710 | + default: |
| 1711 | + snprintf(ctx->scratch_buf, sizeof(ctx->scratch_buf), "0x%llx", |
| 1712 | + (unsigned long long)full_imm); |
| 1713 | + } |
| 1714 | + return ctx->scratch_buf; |
| 1715 | +} |
| 1716 | + |
| 1717 | +static void func_printf(void *ctx, const char *fmt, va_list args) |
| 1718 | +{ |
| 1719 | + vprintf(fmt, args); |
| 1720 | +} |
| 1721 | + |
| 1722 | +static int prep_prog_info(struct bpf_prog_info *info) |
| 1723 | +{ |
| 1724 | + struct bpf_prog_info holder = {}; |
| 1725 | + size_t needed = 0; |
| 1726 | + void *ptr; |
| 1727 | + |
| 1728 | + holder.xlated_prog_len = info->xlated_prog_len; |
| 1729 | + needed += info->xlated_prog_len; |
| 1730 | + |
| 1731 | + holder.nr_jited_ksyms = info->nr_jited_ksyms; |
| 1732 | + needed += info->nr_jited_ksyms * sizeof(__u64); |
| 1733 | + |
| 1734 | + holder.nr_jited_func_lens = info->nr_jited_func_lens; |
| 1735 | + needed += info->nr_jited_func_lens * sizeof(__u32); |
| 1736 | + |
| 1737 | + holder.nr_func_info = info->nr_func_info; |
| 1738 | + holder.func_info_rec_size = info->func_info_rec_size; |
| 1739 | + needed += info->nr_func_info * info->func_info_rec_size; |
| 1740 | + |
| 1741 | + holder.nr_line_info = info->nr_line_info; |
| 1742 | + holder.line_info_rec_size = info->line_info_rec_size; |
| 1743 | + needed += info->nr_line_info * info->line_info_rec_size; |
| 1744 | + |
| 1745 | + holder.nr_jited_line_info = info->nr_jited_line_info; |
| 1746 | + holder.jited_line_info_rec_size = info->jited_line_info_rec_size; |
| 1747 | + needed += info->nr_jited_line_info * info->jited_line_info_rec_size; |
| 1748 | + ptr = malloc(needed); |
| 1749 | + if (!ptr) |
| 1750 | + return -ENOMEM; |
| 1751 | + |
| 1752 | + holder.xlated_prog_insns = (unsigned long)(ptr); |
| 1753 | + ptr += holder.xlated_prog_len; |
| 1754 | + |
| 1755 | + holder.jited_ksyms = (unsigned long)(ptr); |
| 1756 | + ptr += holder.nr_jited_ksyms * sizeof(__u64); |
| 1757 | + |
| 1758 | + holder.jited_func_lens = (unsigned long)(ptr); |
| 1759 | + ptr += holder.nr_jited_func_lens * sizeof(__u32); |
| 1760 | + |
| 1761 | + holder.func_info = (unsigned long)(ptr); |
| 1762 | + ptr += holder.nr_func_info * holder.func_info_rec_size; |
| 1763 | + |
| 1764 | + holder.line_info = (unsigned long)(ptr); |
| 1765 | + ptr += holder.nr_line_info * holder.line_info_rec_size; |
| 1766 | + |
| 1767 | + holder.jited_line_info = (unsigned long)(ptr); |
| 1768 | + ptr += holder.nr_jited_line_info * holder.jited_line_info_rec_size; |
| 1769 | + |
| 1770 | + *info = holder; |
| 1771 | + return 0; |
| 1772 | +} |
| 1773 | + |
| 1774 | +static void emit_line_info(const struct btf *btf, const struct bpf_line_info *linfo) |
| 1775 | +{ |
| 1776 | + const char *line = btf__name_by_offset(btf, linfo->line_off); |
| 1777 | + |
| 1778 | + if (!line) |
| 1779 | + return; |
| 1780 | + line = ltrim(line); |
| 1781 | + printf("; %s\n", line); |
| 1782 | +} |
| 1783 | + |
| 1784 | +static void emit_func_info(struct btf_dump *d, const struct btf *btf, |
| 1785 | + const struct bpf_func_info *finfo) |
| 1786 | +{ |
| 1787 | + LIBBPF_OPTS(btf_dump_emit_type_decl_opts, emit_opts); |
| 1788 | + const struct btf_type *t; |
| 1789 | + __u32 name_off; |
| 1790 | + |
| 1791 | + name_off = btf__type_by_id(btf, finfo->type_id)->name_off; |
| 1792 | + emit_opts.field_name = btf__name_by_offset(btf, name_off); |
| 1793 | + if (!emit_opts.field_name) /* field_name can't be NULL */ |
| 1794 | + emit_opts.field_name = "N/A"; |
| 1795 | + t = btf__type_by_id(btf, finfo->type_id); |
| 1796 | + btf_dump__emit_type_decl(d, t->type, &emit_opts); |
| 1797 | + printf(":\n"); |
| 1798 | +} |
| 1799 | + |
| 1800 | +static void dump_xlated(const struct btf *btf, struct bpf_program *prog, struct bpf_prog_info *info) |
| 1801 | +{ |
| 1802 | + const struct bpf_insn *insn; |
| 1803 | + const struct bpf_func_info *finfo; |
| 1804 | + const struct bpf_line_info *linfo; |
| 1805 | + const struct bpf_prog_linfo *prog_linfo; |
| 1806 | + struct btf_dump *d; |
| 1807 | + __u32 nr_skip = 0, i, n; |
| 1808 | + bool double_insn = false; |
| 1809 | + LIBBPF_OPTS(btf_dump_opts, dump_opts); |
| 1810 | + LIBBPF_OPTS(btf_dump_emit_type_decl_opts, emit_opts); |
| 1811 | + struct dump_context ctx = { |
| 1812 | + .info = info, |
| 1813 | + .kernel_syms = NULL, |
| 1814 | + .kernel_sym_cnt = 0, |
| 1815 | + .kfunc_base_addr = 0 |
| 1816 | + }; |
| 1817 | + struct bpf_insn_cbs cbs = { |
| 1818 | + .cb_print = print_insn, |
| 1819 | + .cb_call = print_call, |
| 1820 | + .cb_imm = print_imm, |
| 1821 | + .private_data = &ctx, |
| 1822 | + }; |
| 1823 | + |
| 1824 | + /* load symbols for each prog, as prog load could have added new items */ |
| 1825 | + kernel_syms_load(&ctx); |
| 1826 | + |
| 1827 | + prog_linfo = bpf_prog_linfo__new(info); |
| 1828 | + insn = (struct bpf_insn *)info->xlated_prog_insns; |
| 1829 | + finfo = (struct bpf_func_info *)info->func_info; |
| 1830 | + d = btf_dump__new(btf, func_printf, NULL, &dump_opts); |
| 1831 | + n = info->xlated_prog_len / sizeof(*insn); |
| 1832 | + |
| 1833 | + for (i = 0; i < n; i += double_insn ? 2 : 1) { |
| 1834 | + if (d && finfo && finfo->insn_off == i) { |
| 1835 | + emit_func_info(d, btf, finfo); |
| 1836 | + finfo++; |
| 1837 | + } |
| 1838 | + |
| 1839 | + if (prog_linfo) { |
| 1840 | + linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip); |
| 1841 | + if (linfo) { |
| 1842 | + emit_line_info(btf, linfo); |
| 1843 | + nr_skip++; |
| 1844 | + } |
| 1845 | + } |
| 1846 | + printf("%4u: ", i); |
| 1847 | + print_bpf_insn(&cbs, insn + i, false); |
| 1848 | + double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); |
| 1849 | + } |
| 1850 | + |
| 1851 | + kernel_syms_free(&ctx); |
| 1852 | + btf_dump__free(d); |
| 1853 | +} |
| 1854 | + |
| 1855 | +static void dump(const struct btf *btf, struct bpf_program *prog, struct bpf_prog_info *info, |
| 1856 | + int prog_fd) |
| 1857 | +{ |
| 1858 | + int err; |
| 1859 | + __u32 info_len = sizeof(*info); |
| 1860 | + |
| 1861 | + if (!env.dump || prog_fd <= 0) |
| 1862 | + return; |
| 1863 | + |
| 1864 | + err = prep_prog_info(info); |
| 1865 | + if (err) |
| 1866 | + return; |
| 1867 | + |
| 1868 | + if (bpf_prog_get_info_by_fd(prog_fd, info, &info_len) != 0) |
| 1869 | + return; |
| 1870 | + dump_xlated(btf, prog, info); |
| 1871 | +} |
| 1872 | + |
1557 | 1873 | static int process_prog(const char *filename, struct bpf_object *obj, struct bpf_program *prog)
|
1558 | 1874 | {
|
1559 | 1875 | const char *base_filename = basename(strdupa(filename));
|
@@ -1634,6 +1950,7 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
|
1634 | 1950 | stats->stats[JITED_SIZE] = info.jited_prog_len;
|
1635 | 1951 |
|
1636 | 1952 | parse_verif_log(buf, buf_sz, stats);
|
| 1953 | + dump(bpf_object__btf(obj), prog, &info, fd); |
1637 | 1954 |
|
1638 | 1955 | if (env.verbose) {
|
1639 | 1956 | printf("PROCESSING %s/%s, DURATION US: %ld, VERDICT: %s, VERIFIER LOG:\n%s\n",
|
|
0 commit comments