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        if (len < 0)
 102                return NULL;
 103
 104        buff = malloc(len);
 105        if (!buff)
 106                return NULL;
 107
 108        ret = klogctl(CMD_ACTION_READ_ALL, buff, len);
 109        if (ret < 0) {
 110                free(buff);
 111                return NULL;
 112        }
 113
 114        *klen = ret;
 115        return buff;
 116}
 117
 118static char *get_flog_buff(const char *file, unsigned int *klen)
 119{
 120        int fd, ret, len;
 121        struct stat fi;
 122        char *buff;
 123
 124        fd = open(file, O_RDONLY);
 125        if (fd < 0)
 126                return NULL;
 127
 128        ret = fstat(fd, &fi);
 129        if (ret < 0 || !S_ISREG(fi.st_mode))
 130                goto out;
 131
 132        len = fi.st_size + 1;
 133        buff = malloc(len);
 134        if (!buff)
 135                goto out;
 136
 137        memset(buff, 0, len);
 138        ret = read(fd, buff, len - 1);
 139        if (ret <= 0)
 140                goto out_free;
 141
 142        close(fd);
 143        *klen = ret;
 144        return buff;
 145out_free:
 146        free(buff);
 147out:
 148        close(fd);
 149        return NULL;
 150}
 151
 152static char *get_log_buff(const char *file, unsigned int *klen)
 153{
 154        return file ? get_flog_buff(file, klen) : get_klog_buff(klen);
 155}
 156
 157static void put_log_buff(char *buff)
 158{
 159        free(buff);
 160}
 161
 162static unsigned int get_last_jit_image(char *haystack, size_t hlen,
 163                                       uint8_t *image, size_t ilen)
 164{
 165        char *ptr, *pptr, *tmp;
 166        off_t off = 0;
 167        int ret, flen, proglen, pass, ulen = 0;
 168        regmatch_t pmatch[1];
 169        unsigned long base;
 170        regex_t regex;
 171
 172        if (hlen == 0)
 173                return 0;
 174
 175        ret = regcomp(&regex, "flen=[[:alnum:]]+ proglen=[[:digit:]]+ "
 176                      "pass=[[:digit:]]+ image=[[:xdigit:]]+", REG_EXTENDED);
 177        assert(ret == 0);
 178
 179        ptr = haystack;
 180        memset(pmatch, 0, sizeof(pmatch));
 181
 182        while (1) {
 183                ret = regexec(&regex, ptr, 1, pmatch, 0);
 184                if (ret == 0) {
 185                        ptr += pmatch[0].rm_eo;
 186                        off += pmatch[0].rm_eo;
 187                        assert(off < hlen);
 188                } else
 189                        break;
 190        }
 191
 192        ptr = haystack + off - (pmatch[0].rm_eo - pmatch[0].rm_so);
 193        ret = sscanf(ptr, "flen=%d proglen=%d pass=%d image=%lx",
 194                     &flen, &proglen, &pass, &base);
 195        if (ret != 4) {
 196                regfree(&regex);
 197                return 0;
 198        }
 199
 200        tmp = ptr = haystack + off;
 201        while ((ptr = strtok(tmp, "\n")) != NULL && ulen < ilen) {
 202                tmp = NULL;
 203                if (!strstr(ptr, "JIT code"))
 204                        continue;
 205                pptr = ptr;
 206                while ((ptr = strstr(pptr, ":")))
 207                        pptr = ptr + 1;
 208                ptr = pptr;
 209                do {
 210                        image[ulen++] = (uint8_t) strtoul(pptr, &pptr, 16);
 211                        if (ptr == pptr || ulen >= ilen) {
 212                                ulen--;
 213                                break;
 214                        }
 215                        ptr = pptr;
 216                } while (1);
 217        }
 218
 219        assert(ulen == proglen);
 220        printf("%d bytes emitted from JIT compiler (pass:%d, flen:%d)\n",
 221               proglen, pass, flen);
 222        printf("%lx + <x>:\n", base);
 223
 224        regfree(&regex);
 225        return ulen;
 226}
 227
 228static void usage(void)
 229{
 230        printf("Usage: bpf_jit_disasm [...]\n");
 231        printf("       -o          Also display related opcodes (default: off).\n");
 232        printf("       -f <file>   Read last image dump from file or stdin (default: klog).\n");
 233        printf("       -h          Display this help.\n");
 234}
 235
 236int main(int argc, char **argv)
 237{
 238        unsigned int len, klen, opt, opcodes = 0;
 239        static uint8_t image[32768];
 240        char *kbuff, *file = NULL;
 241
 242        while ((opt = getopt(argc, argv, "of:")) != -1) {
 243                switch (opt) {
 244                case 'o':
 245                        opcodes = 1;
 246                        break;
 247                case 'f':
 248                        file = optarg;
 249                        break;
 250                default:
 251                        usage();
 252                        return -1;
 253                }
 254        }
 255
 256        bfd_init();
 257        memset(image, 0, sizeof(image));
 258
 259        kbuff = get_log_buff(file, &klen);
 260        if (!kbuff) {
 261                fprintf(stderr, "Could not retrieve log buffer!\n");
 262                return -1;
 263        }
 264
 265        len = get_last_jit_image(kbuff, klen, image, sizeof(image));
 266        if (len > 0)
 267                get_asm_insns(image, len, opcodes);
 268        else
 269                fprintf(stderr, "No JIT image found!\n");
 270
 271        put_log_buff(kbuff);
 272        return 0;
 273}
 274