1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 /* 3 * Copyright (C) 2018 Netronome Systems, Inc. 4 * 5 * This software is dual licensed under the GNU General License Version 2, 6 * June 1991 as shown in the file COPYING in the top-level directory of this 7 * source tree or the BSD 2-Clause License provided below. You have the 8 * option to license this software under the complete terms of either license. 9 * 10 * The BSD 2-Clause License: 11 * 12 * Redistribution and use in source and binary forms, with or 13 * without modification, are permitted provided that the following 14 * conditions are met: 15 * 16 * 1. Redistributions of source code must retain the above 17 * copyright notice, this list of conditions and the following 18 * disclaimer. 19 * 20 * 2. Redistributions in binary form must reproduce the above 21 * copyright notice, this list of conditions and the following 22 * disclaimer in the documentation and/or other materials 23 * provided with the distribution. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 #define _GNU_SOURCE 39 #include <stdarg.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <sys/types.h> 44 #include <libbpf.h> 45 46 #include "disasm.h" 47 #include "json_writer.h" 48 #include "main.h" 49 #include "xlated_dumper.h" 50 51 static int kernel_syms_cmp(const void *sym_a, const void *sym_b) 52 { 53 return ((struct kernel_sym *)sym_a)->address - 54 ((struct kernel_sym *)sym_b)->address; 55 } 56 57 void kernel_syms_load(struct dump_data *dd) 58 { 59 struct kernel_sym *sym; 60 char buff[256]; 61 void *tmp, *address; 62 FILE *fp; 63 64 fp = fopen("/proc/kallsyms", "r"); 65 if (!fp) 66 return; 67 68 while (!feof(fp)) { 69 if (!fgets(buff, sizeof(buff), fp)) 70 break; 71 tmp = reallocarray(dd->sym_mapping, dd->sym_count + 1, 72 sizeof(*dd->sym_mapping)); 73 if (!tmp) { 74 out: 75 free(dd->sym_mapping); 76 dd->sym_mapping = NULL; 77 fclose(fp); 78 return; 79 } 80 dd->sym_mapping = tmp; 81 sym = &dd->sym_mapping[dd->sym_count]; 82 if (sscanf(buff, "%p %*c %s", &address, sym->name) != 2) 83 continue; 84 sym->address = (unsigned long)address; 85 if (!strcmp(sym->name, "__bpf_call_base")) { 86 dd->address_call_base = sym->address; 87 /* sysctl kernel.kptr_restrict was set */ 88 if (!sym->address) 89 goto out; 90 } 91 if (sym->address) 92 dd->sym_count++; 93 } 94 95 fclose(fp); 96 97 qsort(dd->sym_mapping, dd->sym_count, 98 sizeof(*dd->sym_mapping), kernel_syms_cmp); 99 } 100 101 void kernel_syms_destroy(struct dump_data *dd) 102 { 103 free(dd->sym_mapping); 104 } 105 106 struct kernel_sym *kernel_syms_search(struct dump_data *dd, 107 unsigned long key) 108 { 109 struct kernel_sym sym = { 110 .address = key, 111 }; 112 113 return dd->sym_mapping ? 114 bsearch(&sym, dd->sym_mapping, dd->sym_count, 115 sizeof(*dd->sym_mapping), kernel_syms_cmp) : NULL; 116 } 117 118 static void print_insn(void *private_data, const char *fmt, ...) 119 { 120 va_list args; 121 122 va_start(args, fmt); 123 vprintf(fmt, args); 124 va_end(args); 125 } 126 127 static void 128 print_insn_for_graph(void *private_data, const char *fmt, ...) 129 { 130 char buf[64], *p; 131 va_list args; 132 133 va_start(args, fmt); 134 vsnprintf(buf, sizeof(buf), fmt, args); 135 va_end(args); 136 137 p = buf; 138 while (*p != '\0') { 139 if (*p == '\n') { 140 memmove(p + 3, p, strlen(buf) + 1 - (p - buf)); 141 /* Align each instruction dump row left. */ 142 *p++ = '\\'; 143 *p++ = 'l'; 144 /* Output multiline concatenation. */ 145 *p++ = '\\'; 146 } else if (*p == '<' || *p == '>' || *p == '|' || *p == '&') { 147 memmove(p + 1, p, strlen(buf) + 1 - (p - buf)); 148 /* Escape special character. */ 149 *p++ = '\\'; 150 } 151 152 p++; 153 } 154 155 printf("%s", buf); 156 } 157 158 static void print_insn_json(void *private_data, const char *fmt, ...) 159 { 160 unsigned int l = strlen(fmt); 161 char chomped_fmt[l]; 162 va_list args; 163 164 va_start(args, fmt); 165 if (l > 0) { 166 strncpy(chomped_fmt, fmt, l - 1); 167 chomped_fmt[l - 1] = '\0'; 168 } 169 jsonw_vprintf_enquote(json_wtr, chomped_fmt, args); 170 va_end(args); 171 } 172 173 static const char *print_call_pcrel(struct dump_data *dd, 174 struct kernel_sym *sym, 175 unsigned long address, 176 const struct bpf_insn *insn) 177 { 178 if (!dd->nr_jited_ksyms) 179 /* Do not show address for interpreted programs */ 180 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), 181 "%+d", insn->off); 182 else if (sym) 183 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), 184 "%+d#%s", insn->off, sym->name); 185 else 186 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), 187 "%+d#0x%lx", insn->off, address); 188 return dd->scratch_buff; 189 } 190 191 static const char *print_call_helper(struct dump_data *dd, 192 struct kernel_sym *sym, 193 unsigned long address) 194 { 195 if (sym) 196 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), 197 "%s", sym->name); 198 else 199 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), 200 "0x%lx", address); 201 return dd->scratch_buff; 202 } 203 204 static const char *print_call(void *private_data, 205 const struct bpf_insn *insn) 206 { 207 struct dump_data *dd = private_data; 208 unsigned long address = dd->address_call_base + insn->imm; 209 struct kernel_sym *sym; 210 211 if (insn->src_reg == BPF_PSEUDO_CALL && 212 (__u32) insn->imm < dd->nr_jited_ksyms) 213 address = dd->jited_ksyms[insn->imm]; 214 215 sym = kernel_syms_search(dd, address); 216 if (insn->src_reg == BPF_PSEUDO_CALL) 217 return print_call_pcrel(dd, sym, address, insn); 218 else 219 return print_call_helper(dd, sym, address); 220 } 221 222 static const char *print_imm(void *private_data, 223 const struct bpf_insn *insn, 224 __u64 full_imm) 225 { 226 struct dump_data *dd = private_data; 227 228 if (insn->src_reg == BPF_PSEUDO_MAP_FD) 229 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), 230 "map[id:%u]", insn->imm); 231 else 232 snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), 233 "0x%llx", (unsigned long long)full_imm); 234 return dd->scratch_buff; 235 } 236 237 void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len, 238 bool opcodes, bool linum) 239 { 240 const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo; 241 const struct bpf_insn_cbs cbs = { 242 .cb_print = print_insn_json, 243 .cb_call = print_call, 244 .cb_imm = print_imm, 245 .private_data = dd, 246 }; 247 struct bpf_func_info *record; 248 struct bpf_insn *insn = buf; 249 struct btf *btf = dd->btf; 250 bool double_insn = false; 251 unsigned int nr_skip = 0; 252 char func_sig[1024]; 253 unsigned int i; 254 255 jsonw_start_array(json_wtr); 256 record = dd->func_info; 257 for (i = 0; i < len / sizeof(*insn); i++) { 258 if (double_insn) { 259 double_insn = false; 260 continue; 261 } 262 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); 263 264 jsonw_start_object(json_wtr); 265 266 if (btf && record) { 267 if (record->insn_off == i) { 268 btf_dumper_type_only(btf, record->type_id, 269 func_sig, 270 sizeof(func_sig)); 271 if (func_sig[0] != '\0') { 272 jsonw_name(json_wtr, "proto"); 273 jsonw_string(json_wtr, func_sig); 274 } 275 record = (void *)record + dd->finfo_rec_size; 276 } 277 } 278 279 if (prog_linfo) { 280 const struct bpf_line_info *linfo; 281 282 linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip); 283 if (linfo) { 284 btf_dump_linfo_json(btf, linfo, linum); 285 nr_skip++; 286 } 287 } 288 289 jsonw_name(json_wtr, "disasm"); 290 print_bpf_insn(&cbs, insn + i, true); 291 292 if (opcodes) { 293 jsonw_name(json_wtr, "opcodes"); 294 jsonw_start_object(json_wtr); 295 296 jsonw_name(json_wtr, "code"); 297 jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code); 298 299 jsonw_name(json_wtr, "src_reg"); 300 jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg); 301 302 jsonw_name(json_wtr, "dst_reg"); 303 jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg); 304 305 jsonw_name(json_wtr, "off"); 306 print_hex_data_json((uint8_t *)(&insn[i].off), 2); 307 308 jsonw_name(json_wtr, "imm"); 309 if (double_insn && i < len - 1) 310 print_hex_data_json((uint8_t *)(&insn[i].imm), 311 12); 312 else 313 print_hex_data_json((uint8_t *)(&insn[i].imm), 314 4); 315 jsonw_end_object(json_wtr); 316 } 317 jsonw_end_object(json_wtr); 318 } 319 jsonw_end_array(json_wtr); 320 } 321 322 void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len, 323 bool opcodes, bool linum) 324 { 325 const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo; 326 const struct bpf_insn_cbs cbs = { 327 .cb_print = print_insn, 328 .cb_call = print_call, 329 .cb_imm = print_imm, 330 .private_data = dd, 331 }; 332 struct bpf_func_info *record; 333 struct bpf_insn *insn = buf; 334 struct btf *btf = dd->btf; 335 unsigned int nr_skip = 0; 336 bool double_insn = false; 337 char func_sig[1024]; 338 unsigned int i; 339 340 record = dd->func_info; 341 for (i = 0; i < len / sizeof(*insn); i++) { 342 if (double_insn) { 343 double_insn = false; 344 continue; 345 } 346 347 if (btf && record) { 348 if (record->insn_off == i) { 349 btf_dumper_type_only(btf, record->type_id, 350 func_sig, 351 sizeof(func_sig)); 352 if (func_sig[0] != '\0') 353 printf("%s:\n", func_sig); 354 record = (void *)record + dd->finfo_rec_size; 355 } 356 } 357 358 if (prog_linfo) { 359 const struct bpf_line_info *linfo; 360 361 linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip); 362 if (linfo) { 363 btf_dump_linfo_plain(btf, linfo, "; ", 364 linum); 365 nr_skip++; 366 } 367 } 368 369 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); 370 371 printf("% 4d: ", i); 372 print_bpf_insn(&cbs, insn + i, true); 373 374 if (opcodes) { 375 printf(" "); 376 fprint_hex(stdout, insn + i, 8, " "); 377 if (double_insn && i < len - 1) { 378 printf(" "); 379 fprint_hex(stdout, insn + i + 1, 8, " "); 380 } 381 printf("\n"); 382 } 383 } 384 } 385 386 void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end, 387 unsigned int start_idx) 388 { 389 const struct bpf_insn_cbs cbs = { 390 .cb_print = print_insn_for_graph, 391 .cb_call = print_call, 392 .cb_imm = print_imm, 393 .private_data = dd, 394 }; 395 struct bpf_insn *insn_start = buf_start; 396 struct bpf_insn *insn_end = buf_end; 397 struct bpf_insn *cur = insn_start; 398 399 for (; cur <= insn_end; cur++) { 400 printf("% 4d: ", (int)(cur - insn_start + start_idx)); 401 print_bpf_insn(&cbs, cur, true); 402 if (cur != insn_end) 403 printf(" | "); 404 } 405 } 406