Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Minimal BPF JIT image disassembler
0004  *
0005  * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
0006  * debugging or verification purposes.
0007  *
0008  * To get the disassembly of the JIT code, do the following:
0009  *
0010  *  1) `echo 2 > /proc/sys/net/core/bpf_jit_enable`
0011  *  2) Load a BPF filter (e.g. `tcpdump -p -n -s 0 -i eth1 host 192.168.20.0/24`)
0012  *  3) Run e.g. `bpf_jit_disasm -o` to read out the last JIT code
0013  *
0014  * Copyright 2013 Daniel Borkmann <borkmann@redhat.com>
0015  */
0016 
0017 #include <stdint.h>
0018 #include <stdio.h>
0019 #include <stdlib.h>
0020 #include <assert.h>
0021 #include <unistd.h>
0022 #include <string.h>
0023 #include <bfd.h>
0024 #include <dis-asm.h>
0025 #include <regex.h>
0026 #include <fcntl.h>
0027 #include <sys/klog.h>
0028 #include <sys/types.h>
0029 #include <sys/stat.h>
0030 #include <limits.h>
0031 #include <tools/dis-asm-compat.h>
0032 
0033 #define CMD_ACTION_SIZE_BUFFER      10
0034 #define CMD_ACTION_READ_ALL     3
0035 
0036 static void get_exec_path(char *tpath, size_t size)
0037 {
0038     char *path;
0039     ssize_t len;
0040 
0041     snprintf(tpath, size, "/proc/%d/exe", (int) getpid());
0042     tpath[size - 1] = 0;
0043 
0044     path = strdup(tpath);
0045     assert(path);
0046 
0047     len = readlink(path, tpath, size);
0048     tpath[len] = 0;
0049 
0050     free(path);
0051 }
0052 
0053 static void get_asm_insns(uint8_t *image, size_t len, int opcodes)
0054 {
0055     int count, i, pc = 0;
0056     char tpath[PATH_MAX];
0057     struct disassemble_info info;
0058     disassembler_ftype disassemble;
0059     bfd *bfdf;
0060 
0061     memset(tpath, 0, sizeof(tpath));
0062     get_exec_path(tpath, sizeof(tpath));
0063 
0064     bfdf = bfd_openr(tpath, NULL);
0065     assert(bfdf);
0066     assert(bfd_check_format(bfdf, bfd_object));
0067 
0068     init_disassemble_info_compat(&info, stdout,
0069                      (fprintf_ftype) fprintf,
0070                      fprintf_styled);
0071     info.arch = bfd_get_arch(bfdf);
0072     info.mach = bfd_get_mach(bfdf);
0073     info.buffer = image;
0074     info.buffer_length = len;
0075 
0076     disassemble_init_for_target(&info);
0077 
0078 #ifdef DISASM_FOUR_ARGS_SIGNATURE
0079     disassemble = disassembler(info.arch,
0080                    bfd_big_endian(bfdf),
0081                    info.mach,
0082                    bfdf);
0083 #else
0084     disassemble = disassembler(bfdf);
0085 #endif
0086     assert(disassemble);
0087 
0088     do {
0089         printf("%4x:\t", pc);
0090 
0091         count = disassemble(pc, &info);
0092 
0093         if (opcodes) {
0094             printf("\n\t");
0095             for (i = 0; i < count; ++i)
0096                 printf("%02x ", (uint8_t) image[pc + i]);
0097         }
0098         printf("\n");
0099 
0100         pc += count;
0101     } while(count > 0 && pc < len);
0102 
0103     bfd_close(bfdf);
0104 }
0105 
0106 static char *get_klog_buff(unsigned int *klen)
0107 {
0108     int ret, len;
0109     char *buff;
0110 
0111     len = klogctl(CMD_ACTION_SIZE_BUFFER, NULL, 0);
0112     if (len < 0)
0113         return NULL;
0114 
0115     buff = malloc(len);
0116     if (!buff)
0117         return NULL;
0118 
0119     ret = klogctl(CMD_ACTION_READ_ALL, buff, len);
0120     if (ret < 0) {
0121         free(buff);
0122         return NULL;
0123     }
0124 
0125     *klen = ret;
0126     return buff;
0127 }
0128 
0129 static char *get_flog_buff(const char *file, unsigned int *klen)
0130 {
0131     int fd, ret, len;
0132     struct stat fi;
0133     char *buff;
0134 
0135     fd = open(file, O_RDONLY);
0136     if (fd < 0)
0137         return NULL;
0138 
0139     ret = fstat(fd, &fi);
0140     if (ret < 0 || !S_ISREG(fi.st_mode))
0141         goto out;
0142 
0143     len = fi.st_size + 1;
0144     buff = malloc(len);
0145     if (!buff)
0146         goto out;
0147 
0148     memset(buff, 0, len);
0149     ret = read(fd, buff, len - 1);
0150     if (ret <= 0)
0151         goto out_free;
0152 
0153     close(fd);
0154     *klen = ret;
0155     return buff;
0156 out_free:
0157     free(buff);
0158 out:
0159     close(fd);
0160     return NULL;
0161 }
0162 
0163 static char *get_log_buff(const char *file, unsigned int *klen)
0164 {
0165     return file ? get_flog_buff(file, klen) : get_klog_buff(klen);
0166 }
0167 
0168 static void put_log_buff(char *buff)
0169 {
0170     free(buff);
0171 }
0172 
0173 static uint8_t *get_last_jit_image(char *haystack, size_t hlen,
0174                    unsigned int *ilen)
0175 {
0176     char *ptr, *pptr, *tmp;
0177     off_t off = 0;
0178     unsigned int proglen;
0179     int ret, flen, pass, ulen = 0;
0180     regmatch_t pmatch[1];
0181     unsigned long base;
0182     regex_t regex;
0183     uint8_t *image;
0184 
0185     if (hlen == 0)
0186         return NULL;
0187 
0188     ret = regcomp(&regex, "flen=[[:alnum:]]+ proglen=[[:digit:]]+ "
0189               "pass=[[:digit:]]+ image=[[:xdigit:]]+", REG_EXTENDED);
0190     assert(ret == 0);
0191 
0192     ptr = haystack;
0193     memset(pmatch, 0, sizeof(pmatch));
0194 
0195     while (1) {
0196         ret = regexec(&regex, ptr, 1, pmatch, 0);
0197         if (ret == 0) {
0198             ptr += pmatch[0].rm_eo;
0199             off += pmatch[0].rm_eo;
0200             assert(off < hlen);
0201         } else
0202             break;
0203     }
0204 
0205     ptr = haystack + off - (pmatch[0].rm_eo - pmatch[0].rm_so);
0206     ret = sscanf(ptr, "flen=%d proglen=%u pass=%d image=%lx",
0207              &flen, &proglen, &pass, &base);
0208     if (ret != 4) {
0209         regfree(&regex);
0210         return NULL;
0211     }
0212     if (proglen > 1000000) {
0213         printf("proglen of %d too big, stopping\n", proglen);
0214         return NULL;
0215     }
0216 
0217     image = malloc(proglen);
0218     if (!image) {
0219         printf("Out of memory\n");
0220         return NULL;
0221     }
0222     memset(image, 0, proglen);
0223 
0224     tmp = ptr = haystack + off;
0225     while ((ptr = strtok(tmp, "\n")) != NULL && ulen < proglen) {
0226         tmp = NULL;
0227         if (!strstr(ptr, "JIT code"))
0228             continue;
0229         pptr = ptr;
0230         while ((ptr = strstr(pptr, ":")))
0231             pptr = ptr + 1;
0232         ptr = pptr;
0233         do {
0234             image[ulen++] = (uint8_t) strtoul(pptr, &pptr, 16);
0235             if (ptr == pptr) {
0236                 ulen--;
0237                 break;
0238             }
0239             if (ulen >= proglen)
0240                 break;
0241             ptr = pptr;
0242         } while (1);
0243     }
0244 
0245     assert(ulen == proglen);
0246     printf("%u bytes emitted from JIT compiler (pass:%d, flen:%d)\n",
0247            proglen, pass, flen);
0248     printf("%lx + <x>:\n", base);
0249 
0250     regfree(&regex);
0251     *ilen = ulen;
0252     return image;
0253 }
0254 
0255 static void usage(void)
0256 {
0257     printf("Usage: bpf_jit_disasm [...]\n");
0258     printf("       -o          Also display related opcodes (default: off).\n");
0259     printf("       -O <file>   Write binary image of code to file, don't disassemble to stdout.\n");
0260     printf("       -f <file>   Read last image dump from file or stdin (default: klog).\n");
0261     printf("       -h          Display this help.\n");
0262 }
0263 
0264 int main(int argc, char **argv)
0265 {
0266     unsigned int len, klen, opt, opcodes = 0;
0267     char *kbuff, *file = NULL;
0268     char *ofile = NULL;
0269     int ofd;
0270     ssize_t nr;
0271     uint8_t *pos;
0272     uint8_t *image = NULL;
0273 
0274     while ((opt = getopt(argc, argv, "of:O:")) != -1) {
0275         switch (opt) {
0276         case 'o':
0277             opcodes = 1;
0278             break;
0279         case 'O':
0280             ofile = optarg;
0281             break;
0282         case 'f':
0283             file = optarg;
0284             break;
0285         default:
0286             usage();
0287             return -1;
0288         }
0289     }
0290 
0291     bfd_init();
0292 
0293     kbuff = get_log_buff(file, &klen);
0294     if (!kbuff) {
0295         fprintf(stderr, "Could not retrieve log buffer!\n");
0296         return -1;
0297     }
0298 
0299     image = get_last_jit_image(kbuff, klen, &len);
0300     if (!image) {
0301         fprintf(stderr, "No JIT image found!\n");
0302         goto done;
0303     }
0304     if (!ofile) {
0305         get_asm_insns(image, len, opcodes);
0306         goto done;
0307     }
0308 
0309     ofd = open(ofile, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
0310     if (ofd < 0) {
0311         fprintf(stderr, "Could not open file %s for writing: ", ofile);
0312         perror(NULL);
0313         goto done;
0314     }
0315     pos = image;
0316     do {
0317         nr = write(ofd, pos, len);
0318         if (nr < 0) {
0319             fprintf(stderr, "Could not write data to %s: ", ofile);
0320             perror(NULL);
0321             goto done;
0322         }
0323         len -= nr;
0324         pos += nr;
0325     } while (len);
0326     close(ofd);
0327 
0328 done:
0329     put_log_buff(kbuff);
0330     free(image);
0331     return 0;
0332 }