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