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