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