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