root/tools/bpf/bpf_jit_disasm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. get_exec_path
  2. get_asm_insns
  3. get_klog_buff
  4. get_flog_buff
  5. get_log_buff
  6. put_log_buff
  7. get_last_jit_image
  8. usage
  9. main

   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 
  35 static 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 
  52 static 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 
 103 static 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 
 126 static 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;
 153 out_free:
 154         free(buff);
 155 out:
 156         close(fd);
 157         return NULL;
 158 }
 159 
 160 static 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 
 165 static void put_log_buff(char *buff)
 166 {
 167         free(buff);
 168 }
 169 
 170 static 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 
 252 static 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 
 261 int 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 
 325 done:
 326         put_log_buff(kbuff);
 327         free(image);
 328         return 0;
 329 }

/* [<][>][^][v][top][bottom][index][help] */