xref: /linux/tools/objtool/disas.c (revision 5f326c88973691232c0e56ced83c199d53d86766)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
4  */
5 
6 #define _GNU_SOURCE
7 #include <fnmatch.h>
8 
9 #include <objtool/arch.h>
10 #include <objtool/check.h>
11 #include <objtool/disas.h>
12 #include <objtool/warn.h>
13 
14 #include <bfd.h>
15 #include <linux/string.h>
16 #include <tools/dis-asm-compat.h>
17 
18 /*
19  * Size of the buffer for storing the result of disassembling
20  * a single instruction.
21  */
22 #define DISAS_RESULT_SIZE	1024
23 
24 struct disas_context {
25 	struct objtool_file *file;
26 	struct instruction *insn;
27 	char result[DISAS_RESULT_SIZE];
28 	disassembler_ftype disassembler;
29 	struct disassemble_info info;
30 };
31 
32 static int sprint_name(char *str, const char *name, unsigned long offset)
33 {
34 	int len;
35 
36 	if (offset)
37 		len = sprintf(str, "%s+0x%lx", name, offset);
38 	else
39 		len = sprintf(str, "%s", name);
40 
41 	return len;
42 }
43 
44 #define DINFO_FPRINTF(dinfo, ...)	\
45 	((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__))
46 
47 static int disas_result_fprintf(struct disas_context *dctx,
48 				const char *fmt, va_list ap)
49 {
50 	char *buf = dctx->result;
51 	int avail, len;
52 
53 	len = strlen(buf);
54 	if (len >= DISAS_RESULT_SIZE - 1) {
55 		WARN_FUNC(dctx->insn->sec, dctx->insn->offset,
56 			  "disassembly buffer is full");
57 		return -1;
58 	}
59 	avail = DISAS_RESULT_SIZE - len;
60 
61 	len = vsnprintf(buf + len, avail, fmt, ap);
62 	if (len < 0 || len >= avail) {
63 		WARN_FUNC(dctx->insn->sec, dctx->insn->offset,
64 			  "disassembly buffer is truncated");
65 		return -1;
66 	}
67 
68 	return 0;
69 }
70 
71 static int disas_fprintf(void *stream, const char *fmt, ...)
72 {
73 	va_list arg;
74 	int rv;
75 
76 	va_start(arg, fmt);
77 	rv = disas_result_fprintf(stream, fmt, arg);
78 	va_end(arg);
79 
80 	return rv;
81 }
82 
83 /*
84  * For init_disassemble_info_compat().
85  */
86 static int disas_fprintf_styled(void *stream,
87 				enum disassembler_style style,
88 				const char *fmt, ...)
89 {
90 	va_list arg;
91 	int rv;
92 
93 	va_start(arg, fmt);
94 	rv = disas_result_fprintf(stream, fmt, arg);
95 	va_end(arg);
96 
97 	return rv;
98 }
99 
100 static void disas_print_addr_sym(struct section *sec, struct symbol *sym,
101 				 bfd_vma addr, struct disassemble_info *dinfo)
102 {
103 	char symstr[1024];
104 	char *str;
105 
106 	if (sym) {
107 		sprint_name(symstr, sym->name, addr - sym->offset);
108 		DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, symstr);
109 	} else {
110 		str = offstr(sec, addr);
111 		DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, str);
112 		free(str);
113 	}
114 }
115 
116 static void disas_print_addr_noreloc(bfd_vma addr,
117 				     struct disassemble_info *dinfo)
118 {
119 	struct disas_context *dctx = dinfo->application_data;
120 	struct instruction *insn = dctx->insn;
121 	struct symbol *sym = NULL;
122 
123 	if (insn->sym && addr >= insn->sym->offset &&
124 	    addr < insn->sym->offset + insn->sym->len) {
125 		sym = insn->sym;
126 	}
127 
128 	disas_print_addr_sym(insn->sec, sym, addr, dinfo);
129 }
130 
131 static void disas_print_addr_reloc(bfd_vma addr, struct disassemble_info *dinfo)
132 {
133 	struct disas_context *dctx = dinfo->application_data;
134 	struct instruction *insn = dctx->insn;
135 	unsigned long offset;
136 	struct reloc *reloc;
137 	char symstr[1024];
138 	char *str;
139 
140 	reloc = find_reloc_by_dest_range(dctx->file->elf, insn->sec,
141 					 insn->offset, insn->len);
142 	if (!reloc) {
143 		/*
144 		 * There is no relocation for this instruction although
145 		 * the address to resolve points to the next instruction.
146 		 * So this is an effective reference to the next IP, for
147 		 * example: "lea 0x0(%rip),%rdi". The kernel can reference
148 		 * the next IP with _THIS_IP_ macro.
149 		 */
150 		DINFO_FPRINTF(dinfo, "0x%lx <_THIS_IP_>", addr);
151 		return;
152 	}
153 
154 	offset = arch_insn_adjusted_addend(insn, reloc);
155 
156 	/*
157 	 * If the relocation symbol is a section name (for example ".bss")
158 	 * then we try to further resolve the name.
159 	 */
160 	if (reloc->sym->type == STT_SECTION) {
161 		str = offstr(reloc->sym->sec, reloc->sym->offset + offset);
162 		DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, str);
163 		free(str);
164 	} else {
165 		sprint_name(symstr, reloc->sym->name, offset);
166 		DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, symstr);
167 	}
168 }
169 
170 /*
171  * Resolve an address into a "<symbol>+<offset>" string.
172  */
173 static void disas_print_address(bfd_vma addr, struct disassemble_info *dinfo)
174 {
175 	struct disas_context *dctx = dinfo->application_data;
176 	struct instruction *insn = dctx->insn;
177 	struct instruction *jump_dest;
178 	struct symbol *sym;
179 	bool is_reloc;
180 
181 	/*
182 	 * If the instruction is a call/jump and it references a
183 	 * destination then this is likely the address we are looking
184 	 * up. So check it first.
185 	 */
186 	jump_dest = insn->jump_dest;
187 	if (jump_dest && jump_dest->sym && jump_dest->offset == addr) {
188 		disas_print_addr_sym(jump_dest->sec, jump_dest->sym,
189 				     addr, dinfo);
190 		return;
191 	}
192 
193 	/*
194 	 * If the address points to the next instruction then there is
195 	 * probably a relocation. It can be a false positive when the
196 	 * current instruction is referencing the address of the next
197 	 * instruction. This particular case will be handled in
198 	 * disas_print_addr_reloc().
199 	 */
200 	is_reloc = (addr == insn->offset + insn->len);
201 
202 	/*
203 	 * The call destination offset can be the address we are looking
204 	 * up, or 0 if there is a relocation.
205 	 */
206 	sym = insn_call_dest(insn);
207 	if (sym && (sym->offset == addr || (sym->offset == 0 && is_reloc))) {
208 		DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, sym->name);
209 		return;
210 	}
211 
212 	if (!is_reloc)
213 		disas_print_addr_noreloc(addr, dinfo);
214 	else
215 		disas_print_addr_reloc(addr, dinfo);
216 }
217 
218 /*
219  * Initialize disassemble info arch, mach (32 or 64-bit) and options.
220  */
221 int disas_info_init(struct disassemble_info *dinfo,
222 		    int arch, int mach32, int mach64,
223 		    const char *options)
224 {
225 	struct disas_context *dctx = dinfo->application_data;
226 	struct objtool_file *file = dctx->file;
227 
228 	dinfo->arch = arch;
229 
230 	switch (file->elf->ehdr.e_ident[EI_CLASS]) {
231 	case ELFCLASS32:
232 		dinfo->mach = mach32;
233 		break;
234 	case ELFCLASS64:
235 		dinfo->mach = mach64;
236 		break;
237 	default:
238 		return -1;
239 	}
240 
241 	dinfo->disassembler_options = options;
242 
243 	return 0;
244 }
245 
246 struct disas_context *disas_context_create(struct objtool_file *file)
247 {
248 	struct disas_context *dctx;
249 	struct disassemble_info *dinfo;
250 	int err;
251 
252 	dctx = malloc(sizeof(*dctx));
253 	if (!dctx) {
254 		WARN("failed to allocate disassembly context");
255 		return NULL;
256 	}
257 
258 	dctx->file = file;
259 	dinfo = &dctx->info;
260 
261 	init_disassemble_info_compat(dinfo, dctx,
262 				     disas_fprintf, disas_fprintf_styled);
263 
264 	dinfo->read_memory_func = buffer_read_memory;
265 	dinfo->print_address_func = disas_print_address;
266 	dinfo->application_data = dctx;
267 
268 	/*
269 	 * bfd_openr() is not used to avoid doing ELF data processing
270 	 * and caching that has already being done. Here, we just need
271 	 * to identify the target file so we call an arch specific
272 	 * function to fill some disassemble info (arch, mach).
273 	 */
274 
275 	dinfo->arch = bfd_arch_unknown;
276 	dinfo->mach = 0;
277 
278 	err = arch_disas_info_init(dinfo);
279 	if (err || dinfo->arch == bfd_arch_unknown || dinfo->mach == 0) {
280 		WARN("failed to init disassembly arch");
281 		goto error;
282 	}
283 
284 	dinfo->endian = (file->elf->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) ?
285 		BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE;
286 
287 	disassemble_init_for_target(dinfo);
288 
289 	dctx->disassembler = disassembler(dinfo->arch,
290 					  dinfo->endian == BFD_ENDIAN_BIG,
291 					  dinfo->mach, NULL);
292 	if (!dctx->disassembler) {
293 		WARN("failed to create disassembler function");
294 		goto error;
295 	}
296 
297 	return dctx;
298 
299 error:
300 	free(dctx);
301 	return NULL;
302 }
303 
304 void disas_context_destroy(struct disas_context *dctx)
305 {
306 	free(dctx);
307 }
308 
309 char *disas_result(struct disas_context *dctx)
310 {
311 	return dctx->result;
312 }
313 
314 #define DISAS_INSN_OFFSET_SPACE		10
315 #define DISAS_INSN_SPACE		60
316 
317 /*
318  * Print a message in the instruction flow. If insn is not NULL then
319  * the instruction address is printed in addition of the message,
320  * otherwise only the message is printed. In all cases, the instruction
321  * itself is not printed.
322  */
323 static int disas_vprint(FILE *stream, struct section *sec, unsigned long offset,
324 			int depth, const char *format, va_list ap)
325 {
326 	const char *addr_str;
327 	int i, n;
328 	int len;
329 
330 	len = sym_name_max_len + DISAS_INSN_OFFSET_SPACE;
331 	if (depth < 0) {
332 		len += depth;
333 		depth = 0;
334 	}
335 
336 	n = 0;
337 
338 	if (sec) {
339 		addr_str = offstr(sec, offset);
340 		n += fprintf(stream, "%6lx:  %-*s  ", offset, len, addr_str);
341 		free((char *)addr_str);
342 	} else {
343 		len += DISAS_INSN_OFFSET_SPACE + 1;
344 		n += fprintf(stream, "%-*s", len, "");
345 	}
346 
347 	/* print vertical bars to show the code flow */
348 	for (i = 0; i < depth; i++)
349 		n += fprintf(stream, "| ");
350 
351 	if (format)
352 		n += vfprintf(stream, format, ap);
353 
354 	return n;
355 }
356 
357 /*
358  * Print a message in the instruction flow. If insn is not NULL then
359  * the instruction address is printed in addition of the message,
360  * otherwise only the message is printed. In all cases, the instruction
361  * itself is not printed.
362  */
363 void disas_print_info(FILE *stream, struct instruction *insn, int depth,
364 		      const char *format, ...)
365 {
366 	struct section *sec;
367 	unsigned long off;
368 	va_list args;
369 
370 	if (insn) {
371 		sec = insn->sec;
372 		off = insn->offset;
373 	} else {
374 		sec = NULL;
375 		off = 0;
376 	}
377 
378 	va_start(args, format);
379 	disas_vprint(stream, sec, off, depth, format, args);
380 	va_end(args);
381 }
382 
383 /*
384  * Print an instruction address (offset and function), the instruction itself
385  * and an optional message.
386  */
387 void disas_print_insn(FILE *stream, struct disas_context *dctx,
388 		      struct instruction *insn, int depth,
389 		      const char *format, ...)
390 {
391 	char fake_nop_insn[32];
392 	const char *insn_str;
393 	bool fake_nop;
394 	va_list args;
395 	int len;
396 
397 	/*
398 	 * Alternative can insert a fake nop, sometimes with no
399 	 * associated section so nothing to disassemble.
400 	 */
401 	fake_nop = (!insn->sec && insn->type == INSN_NOP);
402 	if (fake_nop) {
403 		snprintf(fake_nop_insn, 32, "<fake nop> (%d bytes)", insn->len);
404 		insn_str = fake_nop_insn;
405 	} else {
406 		disas_insn(dctx, insn);
407 		insn_str = disas_result(dctx);
408 	}
409 
410 	/* print the instruction */
411 	len = (depth + 1) * 2 < DISAS_INSN_SPACE ? DISAS_INSN_SPACE - (depth+1) * 2 : 1;
412 	disas_print_info(stream, insn, depth, "%-*s", len, insn_str);
413 
414 	/* print message if any */
415 	if (!format)
416 		return;
417 
418 	if (strcmp(format, "\n") == 0) {
419 		fprintf(stream, "\n");
420 		return;
421 	}
422 
423 	fprintf(stream, " - ");
424 	va_start(args, format);
425 	vfprintf(stream, format, args);
426 	va_end(args);
427 }
428 
429 /*
430  * Disassemble a single instruction. Return the size of the instruction.
431  */
432 size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
433 {
434 	disassembler_ftype disasm = dctx->disassembler;
435 	struct disassemble_info *dinfo = &dctx->info;
436 
437 	dctx->insn = insn;
438 	dctx->result[0] = '\0';
439 
440 	if (insn->type == INSN_NOP) {
441 		DINFO_FPRINTF(dinfo, "nop%d", insn->len);
442 		return insn->len;
443 	}
444 
445 	/*
446 	 * Set the disassembler buffer to read data from the section
447 	 * containing the instruction to disassemble.
448 	 */
449 	dinfo->buffer = insn->sec->data->d_buf;
450 	dinfo->buffer_vma = 0;
451 	dinfo->buffer_length = insn->sec->sh.sh_size;
452 
453 	return disasm(insn->offset, &dctx->info);
454 }
455 
456 /*
457  * Provide a name for the type of alternatives present at the
458  * specified instruction.
459  *
460  * An instruction can have alternatives with different types, for
461  * example alternative instructions and an exception table. In that
462  * case the name for the alternative instructions type is used.
463  *
464  * Return NULL if the instruction as no alternative.
465  */
466 const char *disas_alt_type_name(struct instruction *insn)
467 {
468 	struct alternative *alt;
469 	const char *name;
470 
471 	name = NULL;
472 	for (alt = insn->alts; alt; alt = alt->next) {
473 		if (alt->type == ALT_TYPE_INSTRUCTIONS) {
474 			name = "alternative";
475 			break;
476 		}
477 
478 		switch (alt->type) {
479 		case ALT_TYPE_EX_TABLE:
480 			name = "ex_table";
481 			break;
482 		case ALT_TYPE_JUMP_TABLE:
483 			name = "jump_table";
484 			break;
485 		default:
486 			name = "unknown";
487 			break;
488 		}
489 	}
490 
491 	return name;
492 }
493 
494 /*
495  * Provide a name for an alternative.
496  */
497 char *disas_alt_name(struct alternative *alt)
498 {
499 	char *str = NULL;
500 
501 	switch (alt->type) {
502 
503 	case ALT_TYPE_EX_TABLE:
504 		str = strdup("EXCEPTION");
505 		break;
506 
507 	case ALT_TYPE_JUMP_TABLE:
508 		str = strdup("JUMP");
509 		break;
510 
511 	case ALT_TYPE_INSTRUCTIONS:
512 		/*
513 		 * This is a non-default group alternative. Create a unique
514 		 * name using the offset of the first original and alternative
515 		 * instructions.
516 		 */
517 		asprintf(&str, "ALTERNATIVE %lx.%lx",
518 			 alt->insn->alt_group->orig_group->first_insn->offset,
519 			 alt->insn->alt_group->first_insn->offset);
520 		break;
521 	}
522 
523 	return str;
524 }
525 
526 /*
527  * Disassemble a function.
528  */
529 static void disas_func(struct disas_context *dctx, struct symbol *func)
530 {
531 	struct instruction *insn;
532 	size_t addr;
533 
534 	printf("%s:\n", func->name);
535 	sym_for_each_insn(dctx->file, func, insn) {
536 		addr = insn->offset;
537 		disas_insn(dctx, insn);
538 		printf(" %6lx:  %s+0x%-6lx      %s\n",
539 		       addr, func->name, addr - func->offset,
540 		       disas_result(dctx));
541 	}
542 	printf("\n");
543 }
544 
545 /*
546  * Disassemble all warned functions.
547  */
548 void disas_warned_funcs(struct disas_context *dctx)
549 {
550 	struct symbol *sym;
551 
552 	if (!dctx)
553 		return;
554 
555 	for_each_sym(dctx->file->elf, sym) {
556 		if (sym->warned)
557 			disas_func(dctx, sym);
558 	}
559 }
560 
561 void disas_funcs(struct disas_context *dctx)
562 {
563 	bool disas_all = !strcmp(opts.disas, "*");
564 	struct section *sec;
565 	struct symbol *sym;
566 
567 	for_each_sec(dctx->file->elf, sec) {
568 
569 		if (!(sec->sh.sh_flags & SHF_EXECINSTR))
570 			continue;
571 
572 		sec_for_each_sym(sec, sym) {
573 			/*
574 			 * If the function had a warning and the verbose
575 			 * option is used then the function was already
576 			 * disassemble.
577 			 */
578 			if (opts.verbose && sym->warned)
579 				continue;
580 
581 			if (disas_all || fnmatch(opts.disas, sym->name, 0) == 0)
582 				disas_func(dctx, sym);
583 		}
584 	}
585 }
586