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 uint8_t *get_last_jit_image(char *haystack, size_t hlen,
 163                                   unsigned int *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        uint8_t *image;
 172
 173        if (hlen == 0)
 174                return NULL;
 175
 176        ret = regcomp(&regex, "flen=[[:alnum:]]+ proglen=[[:digit:]]+ "
 177                      "pass=[[:digit:]]+ image=[[:xdigit:]]+", REG_EXTENDED);
 178        assert(ret == 0);
 179
 180        ptr = haystack;
 181        memset(pmatch, 0, sizeof(pmatch));
 182
 183        while (1) {
 184                ret = regexec(&regex, ptr, 1, pmatch, 0);
 185                if (ret == 0) {
 186                        ptr += pmatch[0].rm_eo;
 187                        off += pmatch[0].rm_eo;
 188                        assert(off < hlen);
 189                } else
 190                        break;
 191        }
 192
 193        ptr = haystack + off - (pmatch[0].rm_eo - pmatch[0].rm_so);
 194        ret = sscanf(ptr, "flen=%d proglen=%d pass=%d image=%lx",
 195                     &flen, &proglen, &pass, &base);
 196        if (ret != 4) {
 197                regfree(&regex);
 198                return NULL;
 199        }
 200        if (proglen > 1000000) {
 201                printf("proglen of %d too big, stopping\n", proglen);
 202                return NULL;
 203        }
 204
 205        image = malloc(proglen);
 206        if (!image) {
 207                printf("Out of memory\n");
 208                return NULL;
 209        }
 210        memset(image, 0, proglen);
 211
 212        tmp = ptr = haystack + off;
 213        while ((ptr = strtok(tmp, "\n")) != NULL && ulen < proglen) {
 214                tmp = NULL;
 215                if (!strstr(ptr, "JIT code"))
 216                        continue;
 217                pptr = ptr;
 218                while ((ptr = strstr(pptr, ":")))
 219                        pptr = ptr + 1;
 220                ptr = pptr;
 221                do {
 222                        image[ulen++] = (uint8_t) strtoul(pptr, &pptr, 16);
 223                        if (ptr == pptr) {
 224                                ulen--;
 225                                break;
 226                        }
 227                        if (ulen >= proglen)
 228                                break;
 229                        ptr = pptr;
 230                } while (1);
 231        }
 232
 233        assert(ulen == proglen);
 234        printf("%d bytes emitted from JIT compiler (pass:%d, flen:%d)\n",
 235               proglen, pass, flen);
 236        printf("%lx + <x>:\n", base);
 237
 238        regfree(&regex);
 239        *ilen = ulen;
 240        return image;
 241}
 242
 243static void usage(void)
 244{
 245        printf("Usage: bpf_jit_disasm [...]\n");
 246        printf("       -o          Also display related opcodes (default: off).\n");
 247        printf("       -O <file>   Write binary image of code to file, don't disassemble to stdout.\n");
 248        printf("       -f <file>   Read last image dump from file or stdin (default: klog).\n");
 249        printf("       -h          Display this help.\n");
 250}
 251
 252int main(int argc, char **argv)
 253{
 254        unsigned int len, klen, opt, opcodes = 0;
 255        char *kbuff, *file = NULL;
 256        char *ofile = NULL;
 257        int ofd;
 258        ssize_t nr;
 259        uint8_t *pos;
 260        uint8_t *image = NULL;
 261
 262        while ((opt = getopt(argc, argv, "of:O:")) != -1) {
 263                switch (opt) {
 264                case 'o':
 265                        opcodes = 1;
 266                        break;
 267                case 'O':
 268                        ofile = optarg;
 269                        break;
 270                case 'f':
 271                        file = optarg;
 272                        break;
 273                default:
 274                        usage();
 275                        return -1;
 276                }
 277        }
 278
 279        bfd_init();
 280
 281        kbuff = get_log_buff(file, &klen);
 282        if (!kbuff) {
 283                fprintf(stderr, "Could not retrieve log buffer!\n");
 284                return -1;
 285        }
 286
 287        image = get_last_jit_image(kbuff, klen, &len);
 288        if (!image) {
 289                fprintf(stderr, "No JIT image found!\n");
 290                goto done;
 291        }
 292        if (!ofile) {
 293                get_asm_insns(image, len, opcodes);
 294                goto done;
 295        }
 296
 297        ofd = open(ofile, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
 298        if (ofd < 0) {
 299                fprintf(stderr, "Could not open file %s for writing: ", ofile);
 300                perror(NULL);
 301                goto done;
 302        }
 303        pos = image;
 304        do {
 305                nr = write(ofd, pos, len);
 306                if (nr < 0) {
 307                        fprintf(stderr, "Could not write data to %s: ", ofile);
 308                        perror(NULL);
 309                        goto done;
 310                }
 311                len -= nr;
 312                pos += nr;
 313        } while (len);
 314        close(ofd);
 315
 316done:
 317        put_log_buff(kbuff);
 318        free(image);
 319        return 0;
 320}
 321