linux/tools/net/bpf_jit_disasm.c
<<
>>
Prefs
   1/*
   2 * Minimal BPF JIT image disassembler
   3 *
   4 * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
   5 * debugging or verification purposes.
   6 *
   7 * To get the disassembly of the JIT code, do the following:
   8 *
   9 *  1) `echo 2 > /proc/sys/net/core/bpf_jit_enable`
  10 *  2) Load a BPF filter (e.g. `tcpdump -p -n -s 0 -i eth1 host 192.168.20.0/24`)
  11 *  3) Run e.g. `bpf_jit_disasm -o` to read out the last JIT code
  12 *
  13 * Copyright 2013 Daniel Borkmann <borkmann@redhat.com>
  14 * Licensed under the GNU General Public License, version 2.0 (GPLv2)
  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
  31#define CMD_ACTION_SIZE_BUFFER          10
  32#define CMD_ACTION_READ_ALL             3
  33
  34static void get_exec_path(char *tpath, size_t size)
  35{
  36        char *path;
  37        ssize_t len;
  38
  39        snprintf(tpath, size, "/proc/%d/exe", (int) getpid());
  40        tpath[size - 1] = 0;
  41
  42        path = strdup(tpath);
  43        assert(path);
  44
  45        len = readlink(path, tpath, size);
  46        tpath[len] = 0;
  47
  48        free(path);
  49}
  50
  51static void get_asm_insns(uint8_t *image, size_t len, int opcodes)
  52{
  53        int count, i, pc = 0;
  54        char tpath[256];
  55        struct disassemble_info info;
  56        disassembler_ftype disassemble;
  57        bfd *bfdf;
  58
  59        memset(tpath, 0, sizeof(tpath));
  60        get_exec_path(tpath, sizeof(tpath));
  61
  62        bfdf = bfd_openr(tpath, NULL);
  63        assert(bfdf);
  64        assert(bfd_check_format(bfdf, bfd_object));
  65
  66        init_disassemble_info(&info, stdout, (fprintf_ftype) fprintf);
  67        info.arch = bfd_get_arch(bfdf);
  68        info.mach = bfd_get_mach(bfdf);
  69        info.buffer = image;
  70        info.buffer_length = len;
  71
  72        disassemble_init_for_target(&info);
  73
  74        disassemble = disassembler(bfdf);
  75        assert(disassemble);
  76
  77        do {
  78                printf("%4x:\t", pc);
  79
  80                count = disassemble(pc, &info);
  81
  82                if (opcodes) {
  83                        printf("\n\t");
  84                        for (i = 0; i < count; ++i)
  85                                printf("%02x ", (uint8_t) image[pc + i]);
  86                }
  87                printf("\n");
  88
  89                pc += count;
  90        } while(count > 0 && pc < len);
  91
  92        bfd_close(bfdf);
  93}
  94
  95static char *get_klog_buff(unsigned int *klen)
  96{
  97        int ret, len;
  98        char *buff;
  99
 100        len = klogctl(CMD_ACTION_SIZE_BUFFER, NULL, 0);
 101        buff = malloc(len);
 102        if (!buff)
 103                return NULL;
 104
 105        ret = klogctl(CMD_ACTION_READ_ALL, buff, len);
 106        if (ret < 0) {
 107                free(buff);
 108                return NULL;
 109        }
 110
 111        *klen = ret;
 112        return buff;
 113}
 114
 115static char *get_flog_buff(const char *file, unsigned int *klen)
 116{
 117        int fd, ret, len;
 118        struct stat fi;
 119        char *buff;
 120
 121        fd = open(file, O_RDONLY);
 122        if (fd < 0)
 123                return NULL;
 124
 125        ret = fstat(fd, &fi);
 126        if (ret < 0 || !S_ISREG(fi.st_mode))
 127                goto out;
 128
 129        len = fi.st_size + 1;
 130        buff = malloc(len);
 131        if (!buff)
 132                goto out;
 133
 134        memset(buff, 0, len);
 135        ret = read(fd, buff, len - 1);
 136        if (ret <= 0)
 137                goto out_free;
 138
 139        close(fd);
 140        *klen = ret;
 141        return buff;
 142out_free:
 143        free(buff);
 144out:
 145        close(fd);
 146        return NULL;
 147}
 148
 149static char *get_log_buff(const char *file, unsigned int *klen)
 150{
 151        return file ? get_flog_buff(file, klen) : get_klog_buff(klen);
 152}
 153
 154static void put_log_buff(char *buff)
 155{
 156        free(buff);
 157}
 158
 159static int get_last_jit_image(char *haystack, size_t hlen,
 160                              uint8_t *image, size_t ilen)
 161{
 162        char *ptr, *pptr, *tmp;
 163        off_t off = 0;
 164        int ret, flen, proglen, pass, ulen = 0;
 165        regmatch_t pmatch[1];
 166        unsigned long base;
 167        regex_t regex;
 168
 169        if (hlen == 0)
 170                return 0;
 171
 172        ret = regcomp(&regex, "flen=[[:alnum:]]+ proglen=[[:digit:]]+ "
 173                      "pass=[[:digit:]]+ image=[[:xdigit:]]+", REG_EXTENDED);
 174        assert(ret == 0);
 175
 176        ptr = haystack;
 177        memset(pmatch, 0, sizeof(pmatch));
 178
 179        while (1) {
 180                ret = regexec(&regex, ptr, 1, pmatch, 0);
 181                if (ret == 0) {
 182                        ptr += pmatch[0].rm_eo;
 183                        off += pmatch[0].rm_eo;
 184                        assert(off < hlen);
 185                } else
 186                        break;
 187        }
 188
 189        ptr = haystack + off - (pmatch[0].rm_eo - pmatch[0].rm_so);
 190        ret = sscanf(ptr, "flen=%d proglen=%d pass=%d image=%lx",
 191                     &flen, &proglen, &pass, &base);
 192        if (ret != 4) {
 193                regfree(&regex);
 194                return 0;
 195        }
 196
 197        tmp = ptr = haystack + off;
 198        while ((ptr = strtok(tmp, "\n")) != NULL && ulen < ilen) {
 199                tmp = NULL;
 200                if (!strstr(ptr, "JIT code"))
 201                        continue;
 202                pptr = ptr;
 203                while ((ptr = strstr(pptr, ":")))
 204                        pptr = ptr + 1;
 205                ptr = pptr;
 206                do {
 207                        image[ulen++] = (uint8_t) strtoul(pptr, &pptr, 16);
 208                        if (ptr == pptr || ulen >= ilen) {
 209                                ulen--;
 210                                break;
 211                        }
 212                        ptr = pptr;
 213                } while (1);
 214        }
 215
 216        assert(ulen == proglen);
 217        printf("%d bytes emitted from JIT compiler (pass:%d, flen:%d)\n",
 218               proglen, pass, flen);
 219        printf("%lx + <x>:\n", base);
 220
 221        regfree(&regex);
 222        return ulen;
 223}
 224
 225static void usage(void)
 226{
 227        printf("Usage: bpf_jit_disasm [...]\n");
 228        printf("       -o          Also display related opcodes (default: off).\n");
 229        printf("       -f <file>   Read last image dump from file or stdin (default: klog).\n");
 230        printf("       -h          Display this help.\n");
 231}
 232
 233int main(int argc, char **argv)
 234{
 235        unsigned int len, klen, opt, opcodes = 0;
 236        static uint8_t image[32768];
 237        char *kbuff, *file = NULL;
 238
 239        while ((opt = getopt(argc, argv, "of:")) != -1) {
 240                switch (opt) {
 241                case 'o':
 242                        opcodes = 1;
 243                        break;
 244                case 'f':
 245                        file = optarg;
 246                        break;
 247                default:
 248                        usage();
 249                        return -1;
 250                }
 251        }
 252
 253        bfd_init();
 254        memset(image, 0, sizeof(image));
 255
 256        kbuff = get_log_buff(file, &klen);
 257        if (!kbuff) {
 258                fprintf(stderr, "Could not retrieve log buffer!\n");
 259                return -1;
 260        }
 261
 262        len = get_last_jit_image(kbuff, klen, image, sizeof(image));
 263        if (len > 0)
 264                get_asm_insns(image, len, opcodes);
 265        else
 266                fprintf(stderr, "No JIT image found!\n");
 267
 268        put_log_buff(kbuff);
 269        return 0;
 270}
 271