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 /* 312 * Disassemble a single instruction. Return the size of the instruction. 313 */ 314 size_t disas_insn(struct disas_context *dctx, struct instruction *insn) 315 { 316 disassembler_ftype disasm = dctx->disassembler; 317 struct disassemble_info *dinfo = &dctx->info; 318 319 dctx->insn = insn; 320 dctx->result[0] = '\0'; 321 322 if (insn->type == INSN_NOP) { 323 DINFO_FPRINTF(dinfo, "nop%d", insn->len); 324 return insn->len; 325 } 326 327 /* 328 * Set the disassembler buffer to read data from the section 329 * containing the instruction to disassemble. 330 */ 331 dinfo->buffer = insn->sec->data->d_buf; 332 dinfo->buffer_vma = 0; 333 dinfo->buffer_length = insn->sec->sh.sh_size; 334 335 return disasm(insn->offset, &dctx->info); 336 } 337 338 /* 339 * Disassemble a function. 340 */ 341 static void disas_func(struct disas_context *dctx, struct symbol *func) 342 { 343 struct instruction *insn; 344 size_t addr; 345 346 printf("%s:\n", func->name); 347 sym_for_each_insn(dctx->file, func, insn) { 348 addr = insn->offset; 349 disas_insn(dctx, insn); 350 printf(" %6lx: %s+0x%-6lx %s\n", 351 addr, func->name, addr - func->offset, 352 disas_result(dctx)); 353 } 354 printf("\n"); 355 } 356 357 /* 358 * Disassemble all warned functions. 359 */ 360 void disas_warned_funcs(struct disas_context *dctx) 361 { 362 struct symbol *sym; 363 364 if (!dctx) 365 return; 366 367 for_each_sym(dctx->file->elf, sym) { 368 if (sym->warned) 369 disas_func(dctx, sym); 370 } 371 } 372