xref: /linux/tools/objtool/disas.c (revision 59953303827eceb06d486ba66cc0d71f55ded8ec)
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