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 disassembler_ftype disassembler; 18 struct disassemble_info info; 19 }; 20 21 #define DINFO_FPRINTF(dinfo, ...) \ 22 ((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__)) 23 24 /* 25 * Initialize disassemble info arch, mach (32 or 64-bit) and options. 26 */ 27 int disas_info_init(struct disassemble_info *dinfo, 28 int arch, int mach32, int mach64, 29 const char *options) 30 { 31 struct disas_context *dctx = dinfo->application_data; 32 struct objtool_file *file = dctx->file; 33 34 dinfo->arch = arch; 35 36 switch (file->elf->ehdr.e_ident[EI_CLASS]) { 37 case ELFCLASS32: 38 dinfo->mach = mach32; 39 break; 40 case ELFCLASS64: 41 dinfo->mach = mach64; 42 break; 43 default: 44 return -1; 45 } 46 47 dinfo->disassembler_options = options; 48 49 return 0; 50 } 51 52 struct disas_context *disas_context_create(struct objtool_file *file) 53 { 54 struct disas_context *dctx; 55 struct disassemble_info *dinfo; 56 int err; 57 58 dctx = malloc(sizeof(*dctx)); 59 if (!dctx) { 60 WARN("failed to allocate disassembly context"); 61 return NULL; 62 } 63 64 dctx->file = file; 65 dinfo = &dctx->info; 66 67 init_disassemble_info_compat(dinfo, stdout, 68 (fprintf_ftype)fprintf, 69 fprintf_styled); 70 71 dinfo->read_memory_func = buffer_read_memory; 72 dinfo->application_data = dctx; 73 74 /* 75 * bfd_openr() is not used to avoid doing ELF data processing 76 * and caching that has already being done. Here, we just need 77 * to identify the target file so we call an arch specific 78 * function to fill some disassemble info (arch, mach). 79 */ 80 81 dinfo->arch = bfd_arch_unknown; 82 dinfo->mach = 0; 83 84 err = arch_disas_info_init(dinfo); 85 if (err || dinfo->arch == bfd_arch_unknown || dinfo->mach == 0) { 86 WARN("failed to init disassembly arch"); 87 goto error; 88 } 89 90 dinfo->endian = (file->elf->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) ? 91 BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE; 92 93 disassemble_init_for_target(dinfo); 94 95 dctx->disassembler = disassembler(dinfo->arch, 96 dinfo->endian == BFD_ENDIAN_BIG, 97 dinfo->mach, NULL); 98 if (!dctx->disassembler) { 99 WARN("failed to create disassembler function"); 100 goto error; 101 } 102 103 return dctx; 104 105 error: 106 free(dctx); 107 return NULL; 108 } 109 110 void disas_context_destroy(struct disas_context *dctx) 111 { 112 free(dctx); 113 } 114 115 /* 116 * Disassemble a single instruction. Return the size of the instruction. 117 */ 118 static size_t disas_insn(struct disas_context *dctx, 119 struct instruction *insn) 120 { 121 disassembler_ftype disasm = dctx->disassembler; 122 struct disassemble_info *dinfo = &dctx->info; 123 124 if (insn->type == INSN_NOP) { 125 DINFO_FPRINTF(dinfo, "nop%d", insn->len); 126 return insn->len; 127 } 128 129 /* 130 * Set the disassembler buffer to read data from the section 131 * containing the instruction to disassemble. 132 */ 133 dinfo->buffer = insn->sec->data->d_buf; 134 dinfo->buffer_vma = 0; 135 dinfo->buffer_length = insn->sec->sh.sh_size; 136 137 return disasm(insn->offset, &dctx->info); 138 } 139 140 /* 141 * Disassemble a function. 142 */ 143 static void disas_func(struct disas_context *dctx, struct symbol *func) 144 { 145 struct instruction *insn; 146 size_t addr; 147 148 printf("%s:\n", func->name); 149 sym_for_each_insn(dctx->file, func, insn) { 150 addr = insn->offset; 151 printf(" %6lx: %s+0x%-6lx ", 152 addr, func->name, addr - func->offset); 153 disas_insn(dctx, insn); 154 printf("\n"); 155 } 156 printf("\n"); 157 } 158 159 /* 160 * Disassemble all warned functions. 161 */ 162 void disas_warned_funcs(struct disas_context *dctx) 163 { 164 struct symbol *sym; 165 166 if (!dctx) 167 return; 168 169 for_each_sym(dctx->file->elf, sym) { 170 if (sym->warned) 171 disas_func(dctx, sym); 172 } 173 } 174