1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com> 4 */ 5 6 #define _GNU_SOURCE 7 #include <fnmatch.h> 8 9 #include <objtool/arch.h> 10 #include <objtool/check.h> 11 #include <objtool/disas.h> 12 #include <objtool/warn.h> 13 14 #include <bfd.h> 15 #include <linux/string.h> 16 #include <tools/dis-asm-compat.h> 17 18 /* 19 * Size of the buffer for storing the result of disassembling 20 * a single instruction. 21 */ 22 #define DISAS_RESULT_SIZE 1024 23 24 struct disas_context { 25 struct objtool_file *file; 26 struct instruction *insn; 27 char result[DISAS_RESULT_SIZE]; 28 disassembler_ftype disassembler; 29 struct disassemble_info info; 30 }; 31 32 static int sprint_name(char *str, const char *name, unsigned long offset) 33 { 34 int len; 35 36 if (offset) 37 len = sprintf(str, "%s+0x%lx", name, offset); 38 else 39 len = sprintf(str, "%s", name); 40 41 return len; 42 } 43 44 #define DINFO_FPRINTF(dinfo, ...) \ 45 ((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__)) 46 47 static int disas_result_fprintf(struct disas_context *dctx, 48 const char *fmt, va_list ap) 49 { 50 char *buf = dctx->result; 51 int avail, len; 52 53 len = strlen(buf); 54 if (len >= DISAS_RESULT_SIZE - 1) { 55 WARN_FUNC(dctx->insn->sec, dctx->insn->offset, 56 "disassembly buffer is full"); 57 return -1; 58 } 59 avail = DISAS_RESULT_SIZE - len; 60 61 len = vsnprintf(buf + len, avail, fmt, ap); 62 if (len < 0 || len >= avail) { 63 WARN_FUNC(dctx->insn->sec, dctx->insn->offset, 64 "disassembly buffer is truncated"); 65 return -1; 66 } 67 68 return 0; 69 } 70 71 static int disas_fprintf(void *stream, const char *fmt, ...) 72 { 73 va_list arg; 74 int rv; 75 76 va_start(arg, fmt); 77 rv = disas_result_fprintf(stream, fmt, arg); 78 va_end(arg); 79 80 return rv; 81 } 82 83 /* 84 * For init_disassemble_info_compat(). 85 */ 86 static int disas_fprintf_styled(void *stream, 87 enum disassembler_style style, 88 const char *fmt, ...) 89 { 90 va_list arg; 91 int rv; 92 93 va_start(arg, fmt); 94 rv = disas_result_fprintf(stream, fmt, arg); 95 va_end(arg); 96 97 return rv; 98 } 99 100 static void disas_print_addr_sym(struct section *sec, struct symbol *sym, 101 bfd_vma addr, struct disassemble_info *dinfo) 102 { 103 char symstr[1024]; 104 char *str; 105 106 if (sym) { 107 sprint_name(symstr, sym->name, addr - sym->offset); 108 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, symstr); 109 } else { 110 str = offstr(sec, addr); 111 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, str); 112 free(str); 113 } 114 } 115 116 static void disas_print_addr_noreloc(bfd_vma addr, 117 struct disassemble_info *dinfo) 118 { 119 struct disas_context *dctx = dinfo->application_data; 120 struct instruction *insn = dctx->insn; 121 struct symbol *sym = NULL; 122 123 if (insn->sym && addr >= insn->sym->offset && 124 addr < insn->sym->offset + insn->sym->len) { 125 sym = insn->sym; 126 } 127 128 disas_print_addr_sym(insn->sec, sym, addr, dinfo); 129 } 130 131 static void disas_print_addr_reloc(bfd_vma addr, struct disassemble_info *dinfo) 132 { 133 struct disas_context *dctx = dinfo->application_data; 134 struct instruction *insn = dctx->insn; 135 unsigned long offset; 136 struct reloc *reloc; 137 char symstr[1024]; 138 char *str; 139 140 reloc = find_reloc_by_dest_range(dctx->file->elf, insn->sec, 141 insn->offset, insn->len); 142 if (!reloc) { 143 /* 144 * There is no relocation for this instruction although 145 * the address to resolve points to the next instruction. 146 * So this is an effective reference to the next IP, for 147 * example: "lea 0x0(%rip),%rdi". The kernel can reference 148 * the next IP with _THIS_IP_ macro. 149 */ 150 DINFO_FPRINTF(dinfo, "0x%lx <_THIS_IP_>", addr); 151 return; 152 } 153 154 offset = arch_insn_adjusted_addend(insn, reloc); 155 156 /* 157 * If the relocation symbol is a section name (for example ".bss") 158 * then we try to further resolve the name. 159 */ 160 if (reloc->sym->type == STT_SECTION) { 161 str = offstr(reloc->sym->sec, reloc->sym->offset + offset); 162 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, str); 163 free(str); 164 } else { 165 sprint_name(symstr, reloc->sym->name, offset); 166 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, symstr); 167 } 168 } 169 170 /* 171 * Resolve an address into a "<symbol>+<offset>" string. 172 */ 173 static void disas_print_address(bfd_vma addr, struct disassemble_info *dinfo) 174 { 175 struct disas_context *dctx = dinfo->application_data; 176 struct instruction *insn = dctx->insn; 177 struct instruction *jump_dest; 178 struct symbol *sym; 179 bool is_reloc; 180 181 /* 182 * If the instruction is a call/jump and it references a 183 * destination then this is likely the address we are looking 184 * up. So check it first. 185 */ 186 jump_dest = insn->jump_dest; 187 if (jump_dest && jump_dest->sym && jump_dest->offset == addr) { 188 disas_print_addr_sym(jump_dest->sec, jump_dest->sym, 189 addr, dinfo); 190 return; 191 } 192 193 /* 194 * If the address points to the next instruction then there is 195 * probably a relocation. It can be a false positive when the 196 * current instruction is referencing the address of the next 197 * instruction. This particular case will be handled in 198 * disas_print_addr_reloc(). 199 */ 200 is_reloc = (addr == insn->offset + insn->len); 201 202 /* 203 * The call destination offset can be the address we are looking 204 * up, or 0 if there is a relocation. 205 */ 206 sym = insn_call_dest(insn); 207 if (sym && (sym->offset == addr || (sym->offset == 0 && is_reloc))) { 208 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, sym->name); 209 return; 210 } 211 212 if (!is_reloc) 213 disas_print_addr_noreloc(addr, dinfo); 214 else 215 disas_print_addr_reloc(addr, dinfo); 216 } 217 218 /* 219 * Initialize disassemble info arch, mach (32 or 64-bit) and options. 220 */ 221 int disas_info_init(struct disassemble_info *dinfo, 222 int arch, int mach32, int mach64, 223 const char *options) 224 { 225 struct disas_context *dctx = dinfo->application_data; 226 struct objtool_file *file = dctx->file; 227 228 dinfo->arch = arch; 229 230 switch (file->elf->ehdr.e_ident[EI_CLASS]) { 231 case ELFCLASS32: 232 dinfo->mach = mach32; 233 break; 234 case ELFCLASS64: 235 dinfo->mach = mach64; 236 break; 237 default: 238 return -1; 239 } 240 241 dinfo->disassembler_options = options; 242 243 return 0; 244 } 245 246 struct disas_context *disas_context_create(struct objtool_file *file) 247 { 248 struct disas_context *dctx; 249 struct disassemble_info *dinfo; 250 int err; 251 252 dctx = malloc(sizeof(*dctx)); 253 if (!dctx) { 254 WARN("failed to allocate disassembly context"); 255 return NULL; 256 } 257 258 dctx->file = file; 259 dinfo = &dctx->info; 260 261 init_disassemble_info_compat(dinfo, dctx, 262 disas_fprintf, disas_fprintf_styled); 263 264 dinfo->read_memory_func = buffer_read_memory; 265 dinfo->print_address_func = disas_print_address; 266 dinfo->application_data = dctx; 267 268 /* 269 * bfd_openr() is not used to avoid doing ELF data processing 270 * and caching that has already being done. Here, we just need 271 * to identify the target file so we call an arch specific 272 * function to fill some disassemble info (arch, mach). 273 */ 274 275 dinfo->arch = bfd_arch_unknown; 276 dinfo->mach = 0; 277 278 err = arch_disas_info_init(dinfo); 279 if (err || dinfo->arch == bfd_arch_unknown || dinfo->mach == 0) { 280 WARN("failed to init disassembly arch"); 281 goto error; 282 } 283 284 dinfo->endian = (file->elf->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) ? 285 BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE; 286 287 disassemble_init_for_target(dinfo); 288 289 dctx->disassembler = disassembler(dinfo->arch, 290 dinfo->endian == BFD_ENDIAN_BIG, 291 dinfo->mach, NULL); 292 if (!dctx->disassembler) { 293 WARN("failed to create disassembler function"); 294 goto error; 295 } 296 297 return dctx; 298 299 error: 300 free(dctx); 301 return NULL; 302 } 303 304 void disas_context_destroy(struct disas_context *dctx) 305 { 306 free(dctx); 307 } 308 309 char *disas_result(struct disas_context *dctx) 310 { 311 return dctx->result; 312 } 313 314 #define DISAS_INSN_OFFSET_SPACE 10 315 #define DISAS_INSN_SPACE 60 316 317 /* 318 * Print a message in the instruction flow. If insn is not NULL then 319 * the instruction address is printed in addition of the message, 320 * otherwise only the message is printed. In all cases, the instruction 321 * itself is not printed. 322 */ 323 static int disas_vprint(FILE *stream, struct section *sec, unsigned long offset, 324 int depth, const char *format, va_list ap) 325 { 326 const char *addr_str; 327 int i, n; 328 int len; 329 330 len = sym_name_max_len + DISAS_INSN_OFFSET_SPACE; 331 if (depth < 0) { 332 len += depth; 333 depth = 0; 334 } 335 336 n = 0; 337 338 if (sec) { 339 addr_str = offstr(sec, offset); 340 n += fprintf(stream, "%6lx: %-*s ", offset, len, addr_str); 341 free((char *)addr_str); 342 } else { 343 len += DISAS_INSN_OFFSET_SPACE + 1; 344 n += fprintf(stream, "%-*s", len, ""); 345 } 346 347 /* print vertical bars to show the code flow */ 348 for (i = 0; i < depth; i++) 349 n += fprintf(stream, "| "); 350 351 if (format) 352 n += vfprintf(stream, format, ap); 353 354 return n; 355 } 356 357 /* 358 * Print a message in the instruction flow. If insn is not NULL then 359 * the instruction address is printed in addition of the message, 360 * otherwise only the message is printed. In all cases, the instruction 361 * itself is not printed. 362 */ 363 void disas_print_info(FILE *stream, struct instruction *insn, int depth, 364 const char *format, ...) 365 { 366 struct section *sec; 367 unsigned long off; 368 va_list args; 369 370 if (insn) { 371 sec = insn->sec; 372 off = insn->offset; 373 } else { 374 sec = NULL; 375 off = 0; 376 } 377 378 va_start(args, format); 379 disas_vprint(stream, sec, off, depth, format, args); 380 va_end(args); 381 } 382 383 /* 384 * Print an instruction address (offset and function), the instruction itself 385 * and an optional message. 386 */ 387 void disas_print_insn(FILE *stream, struct disas_context *dctx, 388 struct instruction *insn, int depth, 389 const char *format, ...) 390 { 391 char fake_nop_insn[32]; 392 const char *insn_str; 393 bool fake_nop; 394 va_list args; 395 int len; 396 397 /* 398 * Alternative can insert a fake nop, sometimes with no 399 * associated section so nothing to disassemble. 400 */ 401 fake_nop = (!insn->sec && insn->type == INSN_NOP); 402 if (fake_nop) { 403 snprintf(fake_nop_insn, 32, "<fake nop> (%d bytes)", insn->len); 404 insn_str = fake_nop_insn; 405 } else { 406 disas_insn(dctx, insn); 407 insn_str = disas_result(dctx); 408 } 409 410 /* print the instruction */ 411 len = (depth + 1) * 2 < DISAS_INSN_SPACE ? DISAS_INSN_SPACE - (depth+1) * 2 : 1; 412 disas_print_info(stream, insn, depth, "%-*s", len, insn_str); 413 414 /* print message if any */ 415 if (!format) 416 return; 417 418 if (strcmp(format, "\n") == 0) { 419 fprintf(stream, "\n"); 420 return; 421 } 422 423 fprintf(stream, " - "); 424 va_start(args, format); 425 vfprintf(stream, format, args); 426 va_end(args); 427 } 428 429 /* 430 * Disassemble a single instruction. Return the size of the instruction. 431 */ 432 size_t disas_insn(struct disas_context *dctx, struct instruction *insn) 433 { 434 disassembler_ftype disasm = dctx->disassembler; 435 struct disassemble_info *dinfo = &dctx->info; 436 437 dctx->insn = insn; 438 dctx->result[0] = '\0'; 439 440 if (insn->type == INSN_NOP) { 441 DINFO_FPRINTF(dinfo, "nop%d", insn->len); 442 return insn->len; 443 } 444 445 /* 446 * Set the disassembler buffer to read data from the section 447 * containing the instruction to disassemble. 448 */ 449 dinfo->buffer = insn->sec->data->d_buf; 450 dinfo->buffer_vma = 0; 451 dinfo->buffer_length = insn->sec->sh.sh_size; 452 453 return disasm(insn->offset, &dctx->info); 454 } 455 456 /* 457 * Provide a name for the type of alternatives present at the 458 * specified instruction. 459 * 460 * An instruction can have alternatives with different types, for 461 * example alternative instructions and an exception table. In that 462 * case the name for the alternative instructions type is used. 463 * 464 * Return NULL if the instruction as no alternative. 465 */ 466 const char *disas_alt_type_name(struct instruction *insn) 467 { 468 struct alternative *alt; 469 const char *name; 470 471 name = NULL; 472 for (alt = insn->alts; alt; alt = alt->next) { 473 if (alt->type == ALT_TYPE_INSTRUCTIONS) { 474 name = "alternative"; 475 break; 476 } 477 478 switch (alt->type) { 479 case ALT_TYPE_EX_TABLE: 480 name = "ex_table"; 481 break; 482 case ALT_TYPE_JUMP_TABLE: 483 name = "jump_table"; 484 break; 485 default: 486 name = "unknown"; 487 break; 488 } 489 } 490 491 return name; 492 } 493 494 /* 495 * Provide a name for an alternative. 496 */ 497 char *disas_alt_name(struct alternative *alt) 498 { 499 char *str = NULL; 500 501 switch (alt->type) { 502 503 case ALT_TYPE_EX_TABLE: 504 str = strdup("EXCEPTION"); 505 break; 506 507 case ALT_TYPE_JUMP_TABLE: 508 str = strdup("JUMP"); 509 break; 510 511 case ALT_TYPE_INSTRUCTIONS: 512 /* 513 * This is a non-default group alternative. Create a unique 514 * name using the offset of the first original and alternative 515 * instructions. 516 */ 517 asprintf(&str, "ALTERNATIVE %lx.%lx", 518 alt->insn->alt_group->orig_group->first_insn->offset, 519 alt->insn->alt_group->first_insn->offset); 520 break; 521 } 522 523 return str; 524 } 525 526 /* 527 * Disassemble a function. 528 */ 529 static void disas_func(struct disas_context *dctx, struct symbol *func) 530 { 531 struct instruction *insn; 532 size_t addr; 533 534 printf("%s:\n", func->name); 535 sym_for_each_insn(dctx->file, func, insn) { 536 addr = insn->offset; 537 disas_insn(dctx, insn); 538 printf(" %6lx: %s+0x%-6lx %s\n", 539 addr, func->name, addr - func->offset, 540 disas_result(dctx)); 541 } 542 printf("\n"); 543 } 544 545 /* 546 * Disassemble all warned functions. 547 */ 548 void disas_warned_funcs(struct disas_context *dctx) 549 { 550 struct symbol *sym; 551 552 if (!dctx) 553 return; 554 555 for_each_sym(dctx->file->elf, sym) { 556 if (sym->warned) 557 disas_func(dctx, sym); 558 } 559 } 560 561 void disas_funcs(struct disas_context *dctx) 562 { 563 bool disas_all = !strcmp(opts.disas, "*"); 564 struct section *sec; 565 struct symbol *sym; 566 567 for_each_sec(dctx->file->elf, sec) { 568 569 if (!(sec->sh.sh_flags & SHF_EXECINSTR)) 570 continue; 571 572 sec_for_each_sym(sec, sym) { 573 /* 574 * If the function had a warning and the verbose 575 * option is used then the function was already 576 * disassemble. 577 */ 578 if (opts.verbose && sym->warned) 579 continue; 580 581 if (disas_all || fnmatch(opts.disas, sym->name, 0) == 0) 582 disas_func(dctx, sym); 583 } 584 } 585 } 586