linux/tools/bpf/bpf_jit_disasm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Minimal BPF JIT image disassembler
   4 *
   5 * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
   6 * debugging or verification purposes.
   7 *
   8 * To get the disassembly of the JIT code, do the following:
   9 *
  10 *  1) `echo 2 > /proc/sys/net/core/bpf_jit_enable`
  11 *  2) Load a BPF filter (e.g. `tcpdump -p -n -s 0 -i eth1 host 192.168.20.0/24`)
  12 *  3) Run e.g. `bpf_jit_disasm -o` to read out the last JIT code
  13 *
  14 * Copyright 2013 Daniel Borkmann <borkmann@redhat.com>
  15 */
  16
  17#include <stdint.h>
  18#include <stdio.h>
  19#include <stdlib.h>
  20#include <assert.h>
  21#include <unistd.h>
  22#include <string.h>
  23#include <bfd.h>
  24#include <dis-asm.h>
  25#include <regex.h>
  26#include <fcntl.h>
  27#include <sys/klog.h>
  28#include <sys/types.h>
  29#include <sys/stat.h>
  30#include <limits.h>
  31
  32#define CMD_ACTION_SIZE_BUFFER          10
  33#define CMD_ACTION_READ_ALL             3
  34
  35static void get_exec_path(char *tpath, size_t size)
  36{
  37        char *path;
  38        ssize_t len;
  39
  40        snprintf(tpath, size, "/proc/%d/exe", (int) getpid());
  41        tpath[size - 1] = 0;
  42
  43        path = strdup(tpath);
  44        assert(path);
  45
  46        len = readlink(path, tpath, size);
  47        tpath[len] = 0;
  48
  49        free(path);
  50}
  51
  52static void get_asm_insns(uint8_t *image, size_t len, int opcodes)
  53{
  54        int count, i, pc = 0;
  55        char tpath[PATH_MAX];
  56        struct disassemble_info info;
  57        disassembler_ftype disassemble;
  58        bfd *bfdf;
  59
  60        memset(tpath, 0, sizeof(tpath));
  61        get_exec_path(tpath, sizeof(tpath));
  62
  63        bfdf = bfd_openr(tpath, NULL);
  64        assert(bfdf);
  65        assert(bfd_check_format(bfdf, bfd_object));
  66
  67        init_disassemble_info(&info, stdout, (fprintf_ftype) fprintf);
  68        info.arch = bfd_get_arch(bfdf);
  69        info.mach = bfd_get_mach(bfdf);
  70        info.buffer = image;
  71        info.buffer_length = len;
  72
  73        disassemble_init_for_target(&info);
  74
  75#ifdef DISASM_FOUR_ARGS_SIGNATURE
  76        disassemble = disassembler(info.arch,
  77                                   bfd_big_endian(bfdf),
  78                                   info.mach,
  79                                   bfdf);
  80#else
  81        disassemble = disassembler(bfdf);
  82#endif
  83        assert(disassemble);
  84
  85        do {
  86                printf("%4x:\t", pc);
  87
  88                count = disassemble(pc, &info);
  89
  90                if (opcodes) {
  91                        printf("\n\t");
  92                        for (i = 0; i < count; ++i)
  93                                printf("%02x ", (uint8_t) image[pc + i]);
  94                }
  95                printf("\n");
  96
  97                pc += count;
  98        } while(count > 0 && pc < len);
  99
 100        bfd_close(bfdf);
 101}
 102
 103static char *get_klog_buff(unsigned int *klen)
 104{
 105        int ret, len;
 106        char *buff;
 107
 108        len = klogctl(CMD_ACTION_SIZE_BUFFER, NULL, 0);
 109        if (len < 0)
 110                return NULL;
 111
 112        buff = malloc(len);
 113        if (!buff)
 114                return NULL;
 115
 116        ret = klogctl(CMD_ACTION_READ_ALL, buff, len);
 117        if (ret < 0) {
 118                free(buff);
 119                return NULL;
 120        }
 121
 122        *klen = ret;
 123        return buff;
 124}
 125
 126static char *get_flog_buff(const char *file, unsigned int *klen)
 127{
 128        int fd, ret, len;
 129        struct stat fi;
 130        char *buff;
 131
 132        fd = open(file, O_RDONLY);
 133        if (fd < 0)
 134                return NULL;
 135
 136        ret = fstat(fd, &fi);
 137        if (ret < 0 || !S_ISREG(fi.st_mode))
 138                goto out;
 139
 140        len = fi.st_size + 1;
 141        buff = malloc(len);
 142        if (!buff)
 143                goto out;
 144
 145        memset(buff, 0, len);
 146        ret = read(fd, buff, len - 1);
 147        if (ret <= 0)
 148                goto out_free;
 149
 150        close(fd);
 151        *klen = ret;
 152        return buff;
 153out_free:
 154        free(buff);
 155out:
 156        close(fd);
 157        return NULL;
 158}
 159
 160static char *get_log_buff(const char *file, unsigned int *klen)
 161{
 162        return file ? get_flog_buff(file, klen) : get_klog_buff(klen);
 163}
 164
 165static void put_log_buff(char *buff)
 166{
 167        free(buff);
 168}
 169
 170static uint8_t *get_last_jit_image(char *haystack, size_t hlen,
 171                                   unsigned int *ilen)
 172{
 173        char *ptr, *pptr, *tmp;
 174        off_t off = 0;
 175        unsigned int proglen;
 176        int ret, flen, pass, ulen = 0;
 177        regmatch_t pmatch[1];
 178        unsigned long base;
 179        regex_t regex;
 180        uint8_t *image;
 181
 182        if (hlen == 0)
 183                return NULL;
 184
 185        ret = regcomp(&regex, "flen=[[:alnum:]]+ proglen=[[:digit:]]+ "
 186                      "pass=[[:digit:]]+ image=[[:xdigit:]]+", REG_EXTENDED);
 187        assert(ret == 0);
 188
 189        ptr = haystack;
 190        memset(pmatch, 0, sizeof(pmatch));
 191
 192        while (1) {
 193                ret = regexec(&regex, ptr, 1, pmatch, 0);
 194                if (ret == 0) {
 195                        ptr += pmatch[0].rm_eo;
 196                        off += pmatch[0].rm_eo;
 197                        assert(off < hlen);
 198                } else
 199                        break;
 200        }
 201
 202        ptr = haystack + off - (pmatch[0].rm_eo - pmatch[0].rm_so);
 203        ret = sscanf(ptr, "flen=%d proglen=%u pass=%d image=%lx",
 204                     &flen, &proglen, &pass, &base);
 205        if (ret != 4) {
 206                regfree(&regex);
 207                return NULL;
 208        }
 209        if (proglen > 1000000) {
 210                printf("proglen of %d too big, stopping\n", proglen);
 211                return NULL;
 212        }
 213
 214        image = malloc(proglen);
 215        if (!image) {
 216                printf("Out of memory\n");
 217                return NULL;
 218        }
 219        memset(image, 0, proglen);
 220
 221        tmp = ptr = haystack + off;
 222        while ((ptr = strtok(tmp, "\n")) != NULL && ulen < proglen) {
 223                tmp = NULL;
 224                if (!strstr(ptr, "JIT code"))
 225                        continue;
 226                pptr = ptr;
 227                while ((ptr = strstr(pptr, ":")))
 228                        pptr = ptr + 1;
 229                ptr = pptr;
 230                do {
 231                        image[ulen++] = (uint8_t) strtoul(pptr, &pptr, 16);
 232                        if (ptr == pptr) {
 233                                ulen--;
 234                                break;
 235                        }
 236                        if (ulen >= proglen)
 237                                break;
 238                        ptr = pptr;
 239                } while (1);
 240        }
 241
 242        assert(ulen == proglen);
 243        printf("%u bytes emitted from JIT compiler (pass:%d, flen:%d)\n",
 244               proglen, pass, flen);
 245        printf("%lx + <x>:\n", base);
 246
 247        regfree(&regex);
 248        *ilen = ulen;
 249        return image;
 250}
 251
 252static void usage(void)
 253{
 254        printf("Usage: bpf_jit_disasm [...]\n");
 255        printf("       -o          Also display related opcodes (default: off).\n");
 256        printf("       -O <file>   Write binary image of code to file, don't disassemble to stdout.\n");
 257        printf("       -f <file>   Read last image dump from file or stdin (default: klog).\n");
 258        printf("       -h          Display this help.\n");
 259}
 260
 261int main(int argc, char **argv)
 262{
 263        unsigned int len, klen, opt, opcodes = 0;
 264        char *kbuff, *file = NULL;
 265        char *ofile = NULL;
 266        int ofd;
 267        ssize_t nr;
 268        uint8_t *pos;
 269        uint8_t *image = NULL;
 270
 271        while ((opt = getopt(argc, argv, "of:O:")) != -1) {
 272                switch (opt) {
 273                case 'o':
 274                        opcodes = 1;
 275                        break;
 276                case 'O':
 277                        ofile = optarg;
 278                        break;
 279                case 'f':
 280                        file = optarg;
 281                        break;
 282                default:
 283                        usage();
 284                        return -1;
 285                }
 286        }
 287
 288        bfd_init();
 289
 290        kbuff = get_log_buff(file, &klen);
 291        if (!kbuff) {
 292                fprintf(stderr, "Could not retrieve log buffer!\n");
 293                return -1;
 294        }
 295
 296        image = get_last_jit_image(kbuff, klen, &len);
 297        if (!image) {
 298                fprintf(stderr, "No JIT image found!\n");
 299                goto done;
 300        }
 301        if (!ofile) {
 302                get_asm_insns(image, len, opcodes);
 303                goto done;
 304        }
 305
 306        ofd = open(ofile, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
 307        if (ofd < 0) {
 308                fprintf(stderr, "Could not open file %s for writing: ", ofile);
 309                perror(NULL);
 310                goto done;
 311        }
 312        pos = image;
 313        do {
 314                nr = write(ofd, pos, len);
 315                if (nr < 0) {
 316                        fprintf(stderr, "Could not write data to %s: ", ofile);
 317                        perror(NULL);
 318                        goto done;
 319                }
 320                len -= nr;
 321                pos += nr;
 322        } while (len);
 323        close(ofd);
 324
 325done:
 326        put_log_buff(kbuff);
 327        free(image);
 328        return 0;
 329}
 330