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 struct disas_context { 16 struct objtool_file *file; 17 struct instruction *insn; 18 disassembler_ftype disassembler; 19 struct disassemble_info info; 20 }; 21 22 static int sprint_name(char *str, const char *name, unsigned long offset) 23 { 24 int len; 25 26 if (offset) 27 len = sprintf(str, "%s+0x%lx", name, offset); 28 else 29 len = sprintf(str, "%s", name); 30 31 return len; 32 } 33 34 #define DINFO_FPRINTF(dinfo, ...) \ 35 ((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__)) 36 37 static void disas_print_addr_sym(struct section *sec, struct symbol *sym, 38 bfd_vma addr, struct disassemble_info *dinfo) 39 { 40 char symstr[1024]; 41 char *str; 42 43 if (sym) { 44 sprint_name(symstr, sym->name, addr - sym->offset); 45 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, symstr); 46 } else { 47 str = offstr(sec, addr); 48 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, str); 49 free(str); 50 } 51 } 52 53 static void disas_print_addr_noreloc(bfd_vma addr, 54 struct disassemble_info *dinfo) 55 { 56 struct disas_context *dctx = dinfo->application_data; 57 struct instruction *insn = dctx->insn; 58 struct symbol *sym = NULL; 59 60 if (insn->sym && addr >= insn->sym->offset && 61 addr < insn->sym->offset + insn->sym->len) { 62 sym = insn->sym; 63 } 64 65 disas_print_addr_sym(insn->sec, sym, addr, dinfo); 66 } 67 68 static void disas_print_addr_reloc(bfd_vma addr, struct disassemble_info *dinfo) 69 { 70 struct disas_context *dctx = dinfo->application_data; 71 struct instruction *insn = dctx->insn; 72 unsigned long offset; 73 struct reloc *reloc; 74 char symstr[1024]; 75 char *str; 76 77 reloc = find_reloc_by_dest_range(dctx->file->elf, insn->sec, 78 insn->offset, insn->len); 79 if (!reloc) { 80 /* 81 * There is no relocation for this instruction although 82 * the address to resolve points to the next instruction. 83 * So this is an effective reference to the next IP, for 84 * example: "lea 0x0(%rip),%rdi". The kernel can reference 85 * the next IP with _THIS_IP_ macro. 86 */ 87 DINFO_FPRINTF(dinfo, "0x%lx <_THIS_IP_>", addr); 88 return; 89 } 90 91 offset = arch_insn_adjusted_addend(insn, reloc); 92 93 /* 94 * If the relocation symbol is a section name (for example ".bss") 95 * then we try to further resolve the name. 96 */ 97 if (reloc->sym->type == STT_SECTION) { 98 str = offstr(reloc->sym->sec, reloc->sym->offset + offset); 99 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, str); 100 free(str); 101 } else { 102 sprint_name(symstr, reloc->sym->name, offset); 103 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, symstr); 104 } 105 } 106 107 /* 108 * Resolve an address into a "<symbol>+<offset>" string. 109 */ 110 static void disas_print_address(bfd_vma addr, struct disassemble_info *dinfo) 111 { 112 struct disas_context *dctx = dinfo->application_data; 113 struct instruction *insn = dctx->insn; 114 struct instruction *jump_dest; 115 struct symbol *sym; 116 bool is_reloc; 117 118 /* 119 * If the instruction is a call/jump and it references a 120 * destination then this is likely the address we are looking 121 * up. So check it first. 122 */ 123 jump_dest = insn->jump_dest; 124 if (jump_dest && jump_dest->sym && jump_dest->offset == addr) { 125 disas_print_addr_sym(jump_dest->sec, jump_dest->sym, 126 addr, dinfo); 127 return; 128 } 129 130 /* 131 * If the address points to the next instruction then there is 132 * probably a relocation. It can be a false positive when the 133 * current instruction is referencing the address of the next 134 * instruction. This particular case will be handled in 135 * disas_print_addr_reloc(). 136 */ 137 is_reloc = (addr == insn->offset + insn->len); 138 139 /* 140 * The call destination offset can be the address we are looking 141 * up, or 0 if there is a relocation. 142 */ 143 sym = insn_call_dest(insn); 144 if (sym && (sym->offset == addr || (sym->offset == 0 && is_reloc))) { 145 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, sym->name); 146 return; 147 } 148 149 if (!is_reloc) 150 disas_print_addr_noreloc(addr, dinfo); 151 else 152 disas_print_addr_reloc(addr, dinfo); 153 } 154 155 /* 156 * Initialize disassemble info arch, mach (32 or 64-bit) and options. 157 */ 158 int disas_info_init(struct disassemble_info *dinfo, 159 int arch, int mach32, int mach64, 160 const char *options) 161 { 162 struct disas_context *dctx = dinfo->application_data; 163 struct objtool_file *file = dctx->file; 164 165 dinfo->arch = arch; 166 167 switch (file->elf->ehdr.e_ident[EI_CLASS]) { 168 case ELFCLASS32: 169 dinfo->mach = mach32; 170 break; 171 case ELFCLASS64: 172 dinfo->mach = mach64; 173 break; 174 default: 175 return -1; 176 } 177 178 dinfo->disassembler_options = options; 179 180 return 0; 181 } 182 183 struct disas_context *disas_context_create(struct objtool_file *file) 184 { 185 struct disas_context *dctx; 186 struct disassemble_info *dinfo; 187 int err; 188 189 dctx = malloc(sizeof(*dctx)); 190 if (!dctx) { 191 WARN("failed to allocate disassembly context"); 192 return NULL; 193 } 194 195 dctx->file = file; 196 dinfo = &dctx->info; 197 198 init_disassemble_info_compat(dinfo, stdout, 199 (fprintf_ftype)fprintf, 200 fprintf_styled); 201 202 dinfo->read_memory_func = buffer_read_memory; 203 dinfo->print_address_func = disas_print_address; 204 dinfo->application_data = dctx; 205 206 /* 207 * bfd_openr() is not used to avoid doing ELF data processing 208 * and caching that has already being done. Here, we just need 209 * to identify the target file so we call an arch specific 210 * function to fill some disassemble info (arch, mach). 211 */ 212 213 dinfo->arch = bfd_arch_unknown; 214 dinfo->mach = 0; 215 216 err = arch_disas_info_init(dinfo); 217 if (err || dinfo->arch == bfd_arch_unknown || dinfo->mach == 0) { 218 WARN("failed to init disassembly arch"); 219 goto error; 220 } 221 222 dinfo->endian = (file->elf->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) ? 223 BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE; 224 225 disassemble_init_for_target(dinfo); 226 227 dctx->disassembler = disassembler(dinfo->arch, 228 dinfo->endian == BFD_ENDIAN_BIG, 229 dinfo->mach, NULL); 230 if (!dctx->disassembler) { 231 WARN("failed to create disassembler function"); 232 goto error; 233 } 234 235 return dctx; 236 237 error: 238 free(dctx); 239 return NULL; 240 } 241 242 void disas_context_destroy(struct disas_context *dctx) 243 { 244 free(dctx); 245 } 246 247 /* 248 * Disassemble a single instruction. Return the size of the instruction. 249 */ 250 static size_t disas_insn(struct disas_context *dctx, 251 struct instruction *insn) 252 { 253 disassembler_ftype disasm = dctx->disassembler; 254 struct disassemble_info *dinfo = &dctx->info; 255 256 dctx->insn = insn; 257 258 if (insn->type == INSN_NOP) { 259 DINFO_FPRINTF(dinfo, "nop%d", insn->len); 260 return insn->len; 261 } 262 263 /* 264 * Set the disassembler buffer to read data from the section 265 * containing the instruction to disassemble. 266 */ 267 dinfo->buffer = insn->sec->data->d_buf; 268 dinfo->buffer_vma = 0; 269 dinfo->buffer_length = insn->sec->sh.sh_size; 270 271 return disasm(insn->offset, &dctx->info); 272 } 273 274 /* 275 * Disassemble a function. 276 */ 277 static void disas_func(struct disas_context *dctx, struct symbol *func) 278 { 279 struct instruction *insn; 280 size_t addr; 281 282 printf("%s:\n", func->name); 283 sym_for_each_insn(dctx->file, func, insn) { 284 addr = insn->offset; 285 printf(" %6lx: %s+0x%-6lx ", 286 addr, func->name, addr - func->offset); 287 disas_insn(dctx, insn); 288 printf("\n"); 289 } 290 printf("\n"); 291 } 292 293 /* 294 * Disassemble all warned functions. 295 */ 296 void disas_warned_funcs(struct disas_context *dctx) 297 { 298 struct symbol *sym; 299 300 if (!dctx) 301 return; 302 303 for_each_sym(dctx->file->elf, sym) { 304 if (sym->warned) 305 disas_func(dctx, sym); 306 } 307 } 308