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 *ctx = LLVMCreateDisasm(triple, NULL, 0, NULL, symbol_lookup_callback); 97 LLVMDisposeMessage(triple); 98 99 if (!*ctx) { 100 p_err("Failed to create disassembler"); 101 return -1; 102 } 103 104 return 0; 105 } 106 107 static void destroy_context(disasm_ctx_t *ctx) 108 { 109 LLVMDisposeMessage(*ctx); 110 } 111 112 static int 113 disassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc, 114 __u64 func_ksym) 115 { 116 char buf[256]; 117 int count; 118 119 count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, func_ksym + pc, 120 buf, sizeof(buf)); 121 if (json_output) 122 printf_json(buf); 123 else 124 printf("%s", buf); 125 126 return count; 127 } 128 129 int disasm_init(void) 130 { 131 LLVMInitializeAllTargetInfos(); 132 LLVMInitializeAllTargetMCs(); 133 LLVMInitializeAllDisassemblers(); 134 return 0; 135 } 136 #endif /* HAVE_LLVM_SUPPORT */ 137 138 #ifdef HAVE_LIBBFD_SUPPORT 139 #define DISASM_SPACER "\t" 140 141 struct disasm_info { 142 struct disassemble_info info; 143 __u64 func_ksym; 144 }; 145 146 static void disasm_print_addr(bfd_vma addr, struct disassemble_info *info) 147 { 148 struct disasm_info *dinfo = container_of(info, struct disasm_info, info); 149 150 addr += dinfo->func_ksym; 151 generic_print_address(addr, info); 152 } 153 154 typedef struct { 155 struct disasm_info *info; 156 disassembler_ftype disassemble; 157 bfd *bfdf; 158 } disasm_ctx_t; 159 160 static int get_exec_path(char *tpath, size_t size) 161 { 162 const char *path = "/proc/self/exe"; 163 ssize_t len; 164 165 len = readlink(path, tpath, size - 1); 166 if (len <= 0) 167 return -1; 168 169 tpath[len] = 0; 170 171 return 0; 172 } 173 174 static int printf_json(void *out, const char *fmt, va_list ap) 175 { 176 char *s; 177 int err; 178 179 err = vasprintf(&s, fmt, ap); 180 if (err < 0) 181 return -1; 182 183 if (!oper_count) { 184 int i; 185 186 /* Strip trailing spaces */ 187 i = strlen(s) - 1; 188 while (s[i] == ' ') 189 s[i--] = '\0'; 190 191 jsonw_string_field(json_wtr, "operation", s); 192 jsonw_name(json_wtr, "operands"); 193 jsonw_start_array(json_wtr); 194 oper_count++; 195 } else if (!strcmp(fmt, ",")) { 196 /* Skip */ 197 } else { 198 jsonw_string(json_wtr, s); 199 oper_count++; 200 } 201 free(s); 202 return 0; 203 } 204 205 static int fprintf_json(void *out, const char *fmt, ...) 206 { 207 va_list ap; 208 int r; 209 210 va_start(ap, fmt); 211 r = printf_json(out, fmt, ap); 212 va_end(ap); 213 214 return r; 215 } 216 217 static int fprintf_json_styled(void *out, 218 enum disassembler_style style __maybe_unused, 219 const char *fmt, ...) 220 { 221 va_list ap; 222 int r; 223 224 va_start(ap, fmt); 225 r = printf_json(out, fmt, ap); 226 va_end(ap); 227 228 return r; 229 } 230 231 static int init_context(disasm_ctx_t *ctx, const char *arch, 232 const char *disassembler_options, 233 unsigned char *image, ssize_t len, __u64 func_ksym) 234 { 235 struct disassemble_info *info; 236 char tpath[PATH_MAX]; 237 bfd *bfdf; 238 239 memset(tpath, 0, sizeof(tpath)); 240 if (get_exec_path(tpath, sizeof(tpath))) { 241 p_err("failed to create disassembler (get_exec_path)"); 242 return -1; 243 } 244 245 ctx->bfdf = bfd_openr(tpath, NULL); 246 if (!ctx->bfdf) { 247 p_err("failed to create disassembler (bfd_openr)"); 248 return -1; 249 } 250 if (!bfd_check_format(ctx->bfdf, bfd_object)) { 251 p_err("failed to create disassembler (bfd_check_format)"); 252 goto err_close; 253 } 254 bfdf = ctx->bfdf; 255 256 ctx->info = malloc(sizeof(struct disasm_info)); 257 if (!ctx->info) { 258 p_err("mem alloc failed"); 259 goto err_close; 260 } 261 ctx->info->func_ksym = func_ksym; 262 info = &ctx->info->info; 263 264 if (json_output) 265 init_disassemble_info_compat(info, stdout, 266 (fprintf_ftype) fprintf_json, 267 fprintf_json_styled); 268 else 269 init_disassemble_info_compat(info, stdout, 270 (fprintf_ftype) fprintf, 271 fprintf_styled); 272 273 /* Update architecture info for offload. */ 274 if (arch) { 275 const bfd_arch_info_type *inf = bfd_scan_arch(arch); 276 277 if (inf) { 278 bfdf->arch_info = inf; 279 } else { 280 p_err("No libbfd support for %s", arch); 281 goto err_free; 282 } 283 } 284 285 info->arch = bfd_get_arch(bfdf); 286 info->mach = bfd_get_mach(bfdf); 287 if (disassembler_options) 288 info->disassembler_options = disassembler_options; 289 info->buffer = image; 290 info->buffer_length = len; 291 info->print_address_func = disasm_print_addr; 292 293 disassemble_init_for_target(info); 294 295 #ifdef DISASM_FOUR_ARGS_SIGNATURE 296 ctx->disassemble = disassembler(info->arch, 297 bfd_big_endian(bfdf), 298 info->mach, 299 bfdf); 300 #else 301 ctx->disassemble = disassembler(bfdf); 302 #endif 303 if (!ctx->disassemble) { 304 p_err("failed to create disassembler"); 305 goto err_free; 306 } 307 return 0; 308 309 err_free: 310 free(info); 311 err_close: 312 bfd_close(ctx->bfdf); 313 return -1; 314 } 315 316 static void destroy_context(disasm_ctx_t *ctx) 317 { 318 free(ctx->info); 319 bfd_close(ctx->bfdf); 320 } 321 322 static int 323 disassemble_insn(disasm_ctx_t *ctx, __maybe_unused unsigned char *image, 324 __maybe_unused ssize_t len, int pc, 325 __maybe_unused __u64 func_ksym) 326 { 327 return ctx->disassemble(pc, &ctx->info->info); 328 } 329 330 int disasm_init(void) 331 { 332 bfd_init(); 333 return 0; 334 } 335 #endif /* HAVE_LIBBPFD_SUPPORT */ 336 337 int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, 338 const char *arch, const char *disassembler_options, 339 const struct btf *btf, 340 const struct bpf_prog_linfo *prog_linfo, 341 __u64 func_ksym, unsigned int func_idx, 342 bool linum) 343 { 344 const struct bpf_line_info *linfo = NULL; 345 unsigned int nr_skip = 0; 346 int count, i, pc = 0; 347 disasm_ctx_t ctx; 348 349 if (!len) 350 return -1; 351 352 if (init_context(&ctx, arch, disassembler_options, image, len, func_ksym)) 353 return -1; 354 355 if (json_output) 356 jsonw_start_array(json_wtr); 357 do { 358 if (prog_linfo) { 359 linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo, 360 func_ksym + pc, 361 func_idx, 362 nr_skip); 363 if (linfo) 364 nr_skip++; 365 } 366 367 if (json_output) { 368 jsonw_start_object(json_wtr); 369 oper_count = 0; 370 if (linfo) 371 btf_dump_linfo_json(btf, linfo, linum); 372 jsonw_name(json_wtr, "pc"); 373 jsonw_printf(json_wtr, "\"0x%x\"", pc); 374 } else { 375 if (linfo) 376 btf_dump_linfo_plain(btf, linfo, "; ", 377 linum); 378 printf("%4x:" DISASM_SPACER, pc); 379 } 380 381 count = disassemble_insn(&ctx, image, len, pc, func_ksym); 382 383 if (json_output) { 384 /* Operand array, was started in fprintf_json. Before 385 * that, make sure we have a _null_ value if no operand 386 * other than operation code was present. 387 */ 388 if (oper_count == 1) 389 jsonw_null(json_wtr); 390 jsonw_end_array(json_wtr); 391 } 392 393 if (opcodes) { 394 if (json_output) { 395 jsonw_name(json_wtr, "opcodes"); 396 jsonw_start_array(json_wtr); 397 for (i = 0; i < count; ++i) 398 jsonw_printf(json_wtr, "\"0x%02hhx\"", 399 (uint8_t)image[pc + i]); 400 jsonw_end_array(json_wtr); 401 } else { 402 printf("\n\t"); 403 for (i = 0; i < count; ++i) 404 printf("%02x ", 405 (uint8_t)image[pc + i]); 406 } 407 } 408 if (json_output) 409 jsonw_end_object(json_wtr); 410 else 411 printf("\n"); 412 413 pc += count; 414 } while (count > 0 && pc < len); 415 if (json_output) 416 jsonw_end_array(json_wtr); 417 418 destroy_context(&ctx); 419 420 return 0; 421 } 422