1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 /* 3 * Based on: 4 * 5 * Minimal BPF JIT image disassembler 6 * 7 * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for 8 * debugging or verification purposes. 9 * 10 * Copyright 2013 Daniel Borkmann <daniel@iogearbox.net> 11 * Licensed under the GNU General Public License, version 2.0 (GPLv2) 12 */ 13 14 #ifndef _GNU_SOURCE 15 #define _GNU_SOURCE 16 #endif 17 #include <stdio.h> 18 #include <stdarg.h> 19 #include <stdint.h> 20 #include <stdlib.h> 21 #include <unistd.h> 22 #include <string.h> 23 #include <sys/stat.h> 24 #include <limits.h> 25 #include <bpf/libbpf.h> 26 27 #ifdef HAVE_LLVM_SUPPORT 28 #include <llvm-c/Core.h> 29 #include <llvm-c/Disassembler.h> 30 #include <llvm-c/Target.h> 31 #include <llvm-c/TargetMachine.h> 32 #endif 33 34 #ifdef HAVE_LIBBFD_SUPPORT 35 #include <bfd.h> 36 #include <dis-asm.h> 37 #include <tools/dis-asm-compat.h> 38 #endif 39 40 #include "json_writer.h" 41 #include "main.h" 42 43 static int oper_count; 44 45 #ifdef HAVE_LLVM_SUPPORT 46 #define DISASM_SPACER 47 48 typedef LLVMDisasmContextRef disasm_ctx_t; 49 50 static int printf_json(char *s) 51 { 52 s = strtok(s, " \t"); 53 jsonw_string_field(json_wtr, "operation", s); 54 55 jsonw_name(json_wtr, "operands"); 56 jsonw_start_array(json_wtr); 57 oper_count = 1; 58 59 while ((s = strtok(NULL, " \t,()")) != 0) { 60 jsonw_string(json_wtr, s); 61 oper_count++; 62 } 63 return 0; 64 } 65 66 /* This callback to set the ref_type is necessary to have the LLVM disassembler 67 * print PC-relative addresses instead of byte offsets for branch instruction 68 * targets. 69 */ 70 static const char * 71 symbol_lookup_callback(__maybe_unused void *disasm_info, 72 __maybe_unused uint64_t ref_value, 73 uint64_t *ref_type, __maybe_unused uint64_t ref_PC, 74 __maybe_unused const char **ref_name) 75 { 76 *ref_type = LLVMDisassembler_ReferenceType_InOut_None; 77 return NULL; 78 } 79 80 static int 81 init_context(disasm_ctx_t *ctx, const char *arch, 82 __maybe_unused const char *disassembler_options, 83 __maybe_unused unsigned char *image, __maybe_unused ssize_t len, 84 __maybe_unused __u64 func_ksym) 85 { 86 char *triple; 87 88 if (arch) 89 triple = LLVMNormalizeTargetTriple(arch); 90 else 91 triple = LLVMGetDefaultTargetTriple(); 92 if (!triple) { 93 p_err("Failed to retrieve triple"); 94 return -1; 95 } 96 97 /* 98 * Enable all aarch64 ISA extensions so the disassembler can handle any 99 * instruction the kernel JIT might emit (e.g. ARM64 LSE atomics). 100 */ 101 if (!strncmp(triple, "aarch64", 7)) 102 *ctx = LLVMCreateDisasmCPUFeatures(triple, "", "+all", NULL, 0, NULL, 103 symbol_lookup_callback); 104 else 105 *ctx = LLVMCreateDisasm(triple, NULL, 0, NULL, symbol_lookup_callback); 106 LLVMDisposeMessage(triple); 107 108 if (!*ctx) { 109 p_err("Failed to create disassembler"); 110 return -1; 111 } 112 113 return 0; 114 } 115 116 static void destroy_context(disasm_ctx_t *ctx) 117 { 118 LLVMDisposeMessage(*ctx); 119 } 120 121 static int 122 disassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc, 123 __u64 func_ksym) 124 { 125 char buf[256]; 126 int count; 127 128 count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, func_ksym + pc, 129 buf, sizeof(buf)); 130 if (json_output) 131 printf_json(buf); 132 else 133 printf("%s", buf); 134 135 return count; 136 } 137 138 int disasm_init(void) 139 { 140 LLVMInitializeAllTargetInfos(); 141 LLVMInitializeAllTargetMCs(); 142 LLVMInitializeAllDisassemblers(); 143 return 0; 144 } 145 #endif /* HAVE_LLVM_SUPPORT */ 146 147 #ifdef HAVE_LIBBFD_SUPPORT 148 #define DISASM_SPACER "\t" 149 150 struct disasm_info { 151 struct disassemble_info info; 152 __u64 func_ksym; 153 }; 154 155 static void disasm_print_addr(bfd_vma addr, struct disassemble_info *info) 156 { 157 struct disasm_info *dinfo = container_of(info, struct disasm_info, info); 158 159 addr += dinfo->func_ksym; 160 generic_print_address(addr, info); 161 } 162 163 typedef struct { 164 struct disasm_info *info; 165 disassembler_ftype disassemble; 166 bfd *bfdf; 167 } disasm_ctx_t; 168 169 static int get_exec_path(char *tpath, size_t size) 170 { 171 const char *path = "/proc/self/exe"; 172 ssize_t len; 173 174 len = readlink(path, tpath, size - 1); 175 if (len <= 0) 176 return -1; 177 178 tpath[len] = 0; 179 180 return 0; 181 } 182 183 static int printf_json(void *out, const char *fmt, va_list ap) 184 { 185 char *s; 186 int err; 187 188 err = vasprintf(&s, fmt, ap); 189 if (err < 0) 190 return -1; 191 192 if (!oper_count) { 193 int i; 194 195 /* Strip trailing spaces */ 196 i = strlen(s) - 1; 197 while (s[i] == ' ') 198 s[i--] = '\0'; 199 200 jsonw_string_field(json_wtr, "operation", s); 201 jsonw_name(json_wtr, "operands"); 202 jsonw_start_array(json_wtr); 203 oper_count++; 204 } else if (!strcmp(fmt, ",")) { 205 /* Skip */ 206 } else { 207 jsonw_string(json_wtr, s); 208 oper_count++; 209 } 210 free(s); 211 return 0; 212 } 213 214 static int fprintf_json(void *out, const char *fmt, ...) 215 { 216 va_list ap; 217 int r; 218 219 va_start(ap, fmt); 220 r = printf_json(out, fmt, ap); 221 va_end(ap); 222 223 return r; 224 } 225 226 static int fprintf_json_styled(void *out, 227 enum disassembler_style style __maybe_unused, 228 const char *fmt, ...) 229 { 230 va_list ap; 231 int r; 232 233 va_start(ap, fmt); 234 r = printf_json(out, fmt, ap); 235 va_end(ap); 236 237 return r; 238 } 239 240 static int init_context(disasm_ctx_t *ctx, const char *arch, 241 const char *disassembler_options, 242 unsigned char *image, ssize_t len, __u64 func_ksym) 243 { 244 struct disassemble_info *info; 245 char tpath[PATH_MAX]; 246 bfd *bfdf; 247 248 memset(tpath, 0, sizeof(tpath)); 249 if (get_exec_path(tpath, sizeof(tpath))) { 250 p_err("failed to create disassembler (get_exec_path)"); 251 return -1; 252 } 253 254 ctx->bfdf = bfd_openr(tpath, NULL); 255 if (!ctx->bfdf) { 256 p_err("failed to create disassembler (bfd_openr)"); 257 return -1; 258 } 259 if (!bfd_check_format(ctx->bfdf, bfd_object)) { 260 p_err("failed to create disassembler (bfd_check_format)"); 261 goto err_close; 262 } 263 bfdf = ctx->bfdf; 264 265 ctx->info = malloc(sizeof(struct disasm_info)); 266 if (!ctx->info) { 267 p_err("mem alloc failed"); 268 goto err_close; 269 } 270 ctx->info->func_ksym = func_ksym; 271 info = &ctx->info->info; 272 273 if (json_output) 274 init_disassemble_info_compat(info, stdout, 275 (fprintf_ftype) fprintf_json, 276 fprintf_json_styled); 277 else 278 init_disassemble_info_compat(info, stdout, 279 (fprintf_ftype) fprintf, 280 fprintf_styled); 281 282 /* Update architecture info for offload. */ 283 if (arch) { 284 const bfd_arch_info_type *inf = bfd_scan_arch(arch); 285 286 if (inf) { 287 bfdf->arch_info = inf; 288 } else { 289 p_err("No libbfd support for %s", arch); 290 goto err_free; 291 } 292 } 293 294 info->arch = bfd_get_arch(bfdf); 295 info->mach = bfd_get_mach(bfdf); 296 if (disassembler_options) 297 info->disassembler_options = disassembler_options; 298 info->buffer = image; 299 info->buffer_length = len; 300 info->print_address_func = disasm_print_addr; 301 302 disassemble_init_for_target(info); 303 304 #ifdef DISASM_FOUR_ARGS_SIGNATURE 305 ctx->disassemble = disassembler(info->arch, 306 bfd_big_endian(bfdf), 307 info->mach, 308 bfdf); 309 #else 310 ctx->disassemble = disassembler(bfdf); 311 #endif 312 if (!ctx->disassemble) { 313 p_err("failed to create disassembler"); 314 goto err_free; 315 } 316 return 0; 317 318 err_free: 319 free(info); 320 err_close: 321 bfd_close(ctx->bfdf); 322 return -1; 323 } 324 325 static void destroy_context(disasm_ctx_t *ctx) 326 { 327 free(ctx->info); 328 bfd_close(ctx->bfdf); 329 } 330 331 static int 332 disassemble_insn(disasm_ctx_t *ctx, __maybe_unused unsigned char *image, 333 __maybe_unused ssize_t len, int pc, 334 __maybe_unused __u64 func_ksym) 335 { 336 return ctx->disassemble(pc, &ctx->info->info); 337 } 338 339 int disasm_init(void) 340 { 341 bfd_init(); 342 return 0; 343 } 344 #endif /* HAVE_LIBBPFD_SUPPORT */ 345 346 int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, 347 const char *arch, const char *disassembler_options, 348 const struct btf *btf, 349 const struct bpf_prog_linfo *prog_linfo, 350 __u64 func_ksym, unsigned int func_idx, 351 bool linum) 352 { 353 const struct bpf_line_info *linfo = NULL; 354 unsigned int nr_skip = 0; 355 int count, i; 356 unsigned int pc = 0; 357 disasm_ctx_t ctx; 358 359 if (!len) 360 return -1; 361 362 if (init_context(&ctx, arch, disassembler_options, image, len, func_ksym)) 363 return -1; 364 365 if (json_output) 366 jsonw_start_array(json_wtr); 367 do { 368 if (prog_linfo) { 369 linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo, 370 func_ksym + pc, 371 func_idx, 372 nr_skip); 373 if (linfo) 374 nr_skip++; 375 } 376 377 if (json_output) { 378 jsonw_start_object(json_wtr); 379 oper_count = 0; 380 if (linfo) 381 btf_dump_linfo_json(btf, linfo, linum); 382 jsonw_name(json_wtr, "pc"); 383 jsonw_printf(json_wtr, "\"0x%x\"", pc); 384 } else { 385 if (linfo) 386 btf_dump_linfo_plain(btf, linfo, "; ", 387 linum); 388 printf("%4x:" DISASM_SPACER, pc); 389 } 390 391 count = disassemble_insn(&ctx, image, len, pc, func_ksym); 392 393 if (json_output) { 394 /* Operand array, was started in fprintf_json. Before 395 * that, make sure we have a _null_ value if no operand 396 * other than operation code was present. 397 */ 398 if (oper_count == 1) 399 jsonw_null(json_wtr); 400 jsonw_end_array(json_wtr); 401 } 402 403 if (opcodes) { 404 if (json_output) { 405 jsonw_name(json_wtr, "opcodes"); 406 jsonw_start_array(json_wtr); 407 for (i = 0; i < count; ++i) 408 jsonw_printf(json_wtr, "\"0x%02hhx\"", 409 (uint8_t)image[pc + i]); 410 jsonw_end_array(json_wtr); 411 } else { 412 printf("\n\t"); 413 for (i = 0; i < count; ++i) 414 printf("%02x ", 415 (uint8_t)image[pc + i]); 416 } 417 } 418 if (json_output) 419 jsonw_end_object(json_wtr); 420 else 421 printf("\n"); 422 423 pc += count; 424 } while (count > 0 && pc < len); 425 if (json_output) 426 jsonw_end_array(json_wtr); 427 428 destroy_context(&ctx); 429 430 return 0; 431 } 432