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/special.h>
13 #include <objtool/warn.h>
14
15 #include <bfd.h>
16 #include <linux/string.h>
17 #include <tools/dis-asm-compat.h>
18
19 /*
20 * Size of the buffer for storing the result of disassembling
21 * a single instruction.
22 */
23 #define DISAS_RESULT_SIZE 1024
24
25 struct disas_context {
26 struct objtool_file *file;
27 struct instruction *insn;
28 bool alt_applied;
29 char result[DISAS_RESULT_SIZE];
30 disassembler_ftype disassembler;
31 struct disassemble_info info;
32 };
33
34 /*
35 * Maximum number of alternatives
36 */
37 #define DISAS_ALT_MAX 5
38
39 /*
40 * Maximum number of instructions per alternative
41 */
42 #define DISAS_ALT_INSN_MAX 50
43
44 /*
45 * Information to disassemble an alternative
46 */
47 struct disas_alt {
48 struct instruction *orig_insn; /* original instruction */
49 struct alternative *alt; /* alternative or NULL if default code */
50 char *name; /* name for this alternative */
51 int width; /* formatting width */
52 struct {
53 char *str; /* instruction string */
54 int offset; /* instruction offset */
55 int nops; /* number of nops */
56 } insn[DISAS_ALT_INSN_MAX]; /* alternative instructions */
57 int insn_idx; /* index of the next instruction to print */
58 };
59
60 #define DALT_DEFAULT(dalt) (!(dalt)->alt)
61 #define DALT_INSN(dalt) (DALT_DEFAULT(dalt) ? (dalt)->orig_insn : (dalt)->alt->insn)
62 #define DALT_GROUP(dalt) (DALT_INSN(dalt)->alt_group)
63 #define DALT_ALTID(dalt) ((dalt)->orig_insn->offset)
64
65 #define ALT_FLAGS_SHIFT 16
66 #define ALT_FLAG_NOT (1 << 0)
67 #define ALT_FLAG_DIRECT_CALL (1 << 1)
68 #define ALT_FEATURE_MASK ((1 << ALT_FLAGS_SHIFT) - 1)
69
alt_feature(unsigned int ft_flags)70 static int alt_feature(unsigned int ft_flags)
71 {
72 return (ft_flags & ALT_FEATURE_MASK);
73 }
74
alt_flags(unsigned int ft_flags)75 static int alt_flags(unsigned int ft_flags)
76 {
77 return (ft_flags >> ALT_FLAGS_SHIFT);
78 }
79
80 /*
81 * Wrapper around asprintf() to allocate and format a string.
82 * Return the allocated string or NULL on error.
83 */
strfmt(const char * fmt,...)84 static char *strfmt(const char *fmt, ...)
85 {
86 va_list ap;
87 char *str;
88 int rv;
89
90 va_start(ap, fmt);
91 rv = vasprintf(&str, fmt, ap);
92 va_end(ap);
93
94 return rv == -1 ? NULL : str;
95 }
96
sprint_name(char * str,const char * name,unsigned long offset)97 static int sprint_name(char *str, const char *name, unsigned long offset)
98 {
99 int len;
100
101 if (offset)
102 len = sprintf(str, "%s+0x%lx", name, offset);
103 else
104 len = sprintf(str, "%s", name);
105
106 return len;
107 }
108
109 #define DINFO_FPRINTF(dinfo, ...) \
110 ((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__))
111 #define bfd_vma_fmt \
112 __builtin_choose_expr(sizeof(bfd_vma) == sizeof(unsigned long), "%#lx <%s>", "%#llx <%s>")
113
disas_result_fprintf(struct disas_context * dctx,const char * fmt,va_list ap)114 static int disas_result_fprintf(struct disas_context *dctx,
115 const char *fmt, va_list ap)
116 {
117 char *buf = dctx->result;
118 int avail, len;
119
120 len = strlen(buf);
121 if (len >= DISAS_RESULT_SIZE - 1) {
122 WARN_FUNC(dctx->insn->sec, dctx->insn->offset,
123 "disassembly buffer is full");
124 return -1;
125 }
126 avail = DISAS_RESULT_SIZE - len;
127
128 len = vsnprintf(buf + len, avail, fmt, ap);
129 if (len < 0 || len >= avail) {
130 WARN_FUNC(dctx->insn->sec, dctx->insn->offset,
131 "disassembly buffer is truncated");
132 return -1;
133 }
134
135 return 0;
136 }
137
disas_fprintf(void * stream,const char * fmt,...)138 static int disas_fprintf(void *stream, const char *fmt, ...)
139 {
140 va_list arg;
141 int rv;
142
143 va_start(arg, fmt);
144 rv = disas_result_fprintf(stream, fmt, arg);
145 va_end(arg);
146
147 return rv;
148 }
149
150 /*
151 * For init_disassemble_info_compat().
152 */
disas_fprintf_styled(void * stream,enum disassembler_style style,const char * fmt,...)153 static int disas_fprintf_styled(void *stream,
154 enum disassembler_style style,
155 const char *fmt, ...)
156 {
157 va_list arg;
158 int rv;
159
160 va_start(arg, fmt);
161 rv = disas_result_fprintf(stream, fmt, arg);
162 va_end(arg);
163
164 return rv;
165 }
166
disas_print_addr_sym(struct section * sec,struct symbol * sym,bfd_vma addr,struct disassemble_info * dinfo)167 static void disas_print_addr_sym(struct section *sec, struct symbol *sym,
168 bfd_vma addr, struct disassemble_info *dinfo)
169 {
170 char symstr[1024];
171 char *str;
172
173 if (sym) {
174 sprint_name(symstr, sym->name, addr - sym->offset);
175 DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, symstr);
176 } else {
177 str = offstr(sec, addr);
178 DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, str);
179 free(str);
180 }
181 }
182
disas_print_addr_alt(bfd_vma addr,struct disassemble_info * dinfo)183 static bool disas_print_addr_alt(bfd_vma addr, struct disassemble_info *dinfo)
184 {
185 struct disas_context *dctx = dinfo->application_data;
186 struct instruction *orig_first_insn;
187 struct alt_group *alt_group;
188 unsigned long offset;
189 struct symbol *sym;
190
191 /*
192 * Check if we are processing an alternative at the original
193 * instruction address (i.e. if alt_applied is true) and if
194 * we are referencing an address inside the alternative.
195 *
196 * For example, this happens if there is a branch inside an
197 * alternative. In that case, the address should be updated
198 * to a reference inside the original instruction flow.
199 */
200 if (!dctx->alt_applied)
201 return false;
202
203 alt_group = dctx->insn->alt_group;
204 if (!alt_group || !alt_group->orig_group ||
205 addr < alt_group->first_insn->offset ||
206 addr > alt_group->last_insn->offset)
207 return false;
208
209 orig_first_insn = alt_group->orig_group->first_insn;
210 offset = addr - alt_group->first_insn->offset;
211
212 addr = orig_first_insn->offset + offset;
213 sym = orig_first_insn->sym;
214
215 disas_print_addr_sym(orig_first_insn->sec, sym, addr, dinfo);
216
217 return true;
218 }
219
disas_print_addr_noreloc(bfd_vma addr,struct disassemble_info * dinfo)220 static void disas_print_addr_noreloc(bfd_vma addr,
221 struct disassemble_info *dinfo)
222 {
223 struct disas_context *dctx = dinfo->application_data;
224 struct instruction *insn = dctx->insn;
225 struct symbol *sym = NULL;
226
227 if (disas_print_addr_alt(addr, dinfo))
228 return;
229
230 if (insn->sym && addr >= insn->sym->offset &&
231 addr < insn->sym->offset + insn->sym->len) {
232 sym = insn->sym;
233 }
234
235 disas_print_addr_sym(insn->sec, sym, addr, dinfo);
236 }
237
disas_print_addr_reloc(bfd_vma addr,struct disassemble_info * dinfo)238 static void disas_print_addr_reloc(bfd_vma addr, struct disassemble_info *dinfo)
239 {
240 struct disas_context *dctx = dinfo->application_data;
241 struct instruction *insn = dctx->insn;
242 unsigned long offset;
243 struct reloc *reloc;
244 char symstr[1024];
245 char *str;
246
247 reloc = find_reloc_by_dest_range(dctx->file->elf, insn->sec,
248 insn->offset, insn->len);
249 if (!reloc) {
250 /*
251 * There is no relocation for this instruction although
252 * the address to resolve points to the next instruction.
253 * So this is an effective reference to the next IP, for
254 * example: "lea 0x0(%rip),%rdi". The kernel can reference
255 * the next IP with _THIS_IP_ macro.
256 */
257 DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, "_THIS_IP_");
258 return;
259 }
260
261 offset = arch_insn_adjusted_addend(insn, reloc);
262
263 /*
264 * If the relocation symbol is a section name (for example ".bss")
265 * then we try to further resolve the name.
266 */
267 if (reloc->sym->type == STT_SECTION) {
268 str = offstr(reloc->sym->sec, reloc->sym->offset + offset);
269 DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, str);
270 free(str);
271 } else {
272 sprint_name(symstr, reloc->sym->name, offset);
273 DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, symstr);
274 }
275 }
276
277 /*
278 * Resolve an address into a "<symbol>+<offset>" string.
279 */
disas_print_address(bfd_vma addr,struct disassemble_info * dinfo)280 static void disas_print_address(bfd_vma addr, struct disassemble_info *dinfo)
281 {
282 struct disas_context *dctx = dinfo->application_data;
283 struct instruction *insn = dctx->insn;
284 struct instruction *jump_dest;
285 struct symbol *sym;
286 bool is_reloc;
287
288 /*
289 * If the instruction is a call/jump and it references a
290 * destination then this is likely the address we are looking
291 * up. So check it first.
292 */
293 jump_dest = insn->jump_dest;
294 if (jump_dest && jump_dest->sym && jump_dest->offset == addr) {
295 if (!disas_print_addr_alt(addr, dinfo))
296 disas_print_addr_sym(jump_dest->sec, jump_dest->sym,
297 addr, dinfo);
298 return;
299 }
300
301 /*
302 * If the address points to the next instruction then there is
303 * probably a relocation. It can be a false positive when the
304 * current instruction is referencing the address of the next
305 * instruction. This particular case will be handled in
306 * disas_print_addr_reloc().
307 */
308 is_reloc = (addr == insn->offset + insn->len);
309
310 /*
311 * The call destination offset can be the address we are looking
312 * up, or 0 if there is a relocation.
313 */
314 sym = insn_call_dest(insn);
315 if (sym && (sym->offset == addr || (sym->offset == 0 && is_reloc))) {
316 DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, sym->name);
317 return;
318 }
319
320 if (!is_reloc)
321 disas_print_addr_noreloc(addr, dinfo);
322 else
323 disas_print_addr_reloc(addr, dinfo);
324 }
325
326 /*
327 * Initialize disassemble info arch, mach (32 or 64-bit) and options.
328 */
disas_info_init(struct disassemble_info * dinfo,int arch,int mach32,int mach64,const char * options)329 int disas_info_init(struct disassemble_info *dinfo,
330 int arch, int mach32, int mach64,
331 const char *options)
332 {
333 struct disas_context *dctx = dinfo->application_data;
334 struct objtool_file *file = dctx->file;
335
336 dinfo->arch = arch;
337
338 switch (file->elf->ehdr.e_ident[EI_CLASS]) {
339 case ELFCLASS32:
340 dinfo->mach = mach32;
341 break;
342 case ELFCLASS64:
343 dinfo->mach = mach64;
344 break;
345 default:
346 return -1;
347 }
348
349 dinfo->disassembler_options = options;
350
351 return 0;
352 }
353
disas_context_create(struct objtool_file * file)354 struct disas_context *disas_context_create(struct objtool_file *file)
355 {
356 struct disas_context *dctx;
357 struct disassemble_info *dinfo;
358 int err;
359
360 dctx = malloc(sizeof(*dctx));
361 if (!dctx) {
362 WARN("failed to allocate disassembly context");
363 return NULL;
364 }
365
366 dctx->file = file;
367 dinfo = &dctx->info;
368
369 init_disassemble_info_compat(dinfo, dctx,
370 disas_fprintf, disas_fprintf_styled);
371
372 dinfo->read_memory_func = buffer_read_memory;
373 dinfo->print_address_func = disas_print_address;
374 dinfo->application_data = dctx;
375
376 /*
377 * bfd_openr() is not used to avoid doing ELF data processing
378 * and caching that has already being done. Here, we just need
379 * to identify the target file so we call an arch specific
380 * function to fill some disassemble info (arch, mach).
381 */
382
383 dinfo->arch = bfd_arch_unknown;
384 dinfo->mach = 0;
385
386 err = arch_disas_info_init(dinfo);
387 if (err || dinfo->arch == bfd_arch_unknown || dinfo->mach == 0) {
388 WARN("failed to init disassembly arch");
389 goto error;
390 }
391
392 dinfo->endian = (file->elf->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) ?
393 BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE;
394
395 disassemble_init_for_target(dinfo);
396
397 dctx->disassembler = disassembler(dinfo->arch,
398 dinfo->endian == BFD_ENDIAN_BIG,
399 dinfo->mach, NULL);
400 if (!dctx->disassembler) {
401 WARN("failed to create disassembler function");
402 goto error;
403 }
404
405 return dctx;
406
407 error:
408 free(dctx);
409 return NULL;
410 }
411
disas_context_destroy(struct disas_context * dctx)412 void disas_context_destroy(struct disas_context *dctx)
413 {
414 free(dctx);
415 }
416
disas_result(struct disas_context * dctx)417 char *disas_result(struct disas_context *dctx)
418 {
419 return dctx->result;
420 }
421
422 #define DISAS_INSN_OFFSET_SPACE 10
423 #define DISAS_INSN_SPACE 60
424
425 #define DISAS_PRINSN(dctx, insn, depth) \
426 disas_print_insn(stdout, dctx, insn, depth, "\n")
427
428 /*
429 * Print a message in the instruction flow. If sec is not NULL then the
430 * address at the section offset is printed in addition of the message,
431 * otherwise only the message is printed.
432 */
disas_vprint(FILE * stream,struct section * sec,unsigned long offset,int depth,const char * format,va_list ap)433 static int disas_vprint(FILE *stream, struct section *sec, unsigned long offset,
434 int depth, const char *format, va_list ap)
435 {
436 const char *addr_str;
437 int i, n;
438 int len;
439
440 len = sym_name_max_len + DISAS_INSN_OFFSET_SPACE;
441 if (depth < 0) {
442 len += depth;
443 depth = 0;
444 }
445
446 n = 0;
447
448 if (sec) {
449 addr_str = offstr(sec, offset);
450 n += fprintf(stream, "%6lx: %-*s ", offset, len, addr_str);
451 free((char *)addr_str);
452 } else {
453 len += DISAS_INSN_OFFSET_SPACE + 1;
454 n += fprintf(stream, "%-*s", len, "");
455 }
456
457 /* print vertical bars to show the code flow */
458 for (i = 0; i < depth; i++)
459 n += fprintf(stream, "| ");
460
461 if (format)
462 n += vfprintf(stream, format, ap);
463
464 return n;
465 }
466
disas_print(FILE * stream,struct section * sec,unsigned long offset,int depth,const char * format,...)467 static int disas_print(FILE *stream, struct section *sec, unsigned long offset,
468 int depth, const char *format, ...)
469 {
470 va_list args;
471 int len;
472
473 va_start(args, format);
474 len = disas_vprint(stream, sec, offset, depth, format, args);
475 va_end(args);
476
477 return len;
478 }
479
480 /*
481 * Print a message in the instruction flow. If insn is not NULL then
482 * the instruction address is printed in addition of the message,
483 * otherwise only the message is printed. In all cases, the instruction
484 * itself is not printed.
485 */
disas_print_info(FILE * stream,struct instruction * insn,int depth,const char * format,...)486 void disas_print_info(FILE *stream, struct instruction *insn, int depth,
487 const char *format, ...)
488 {
489 struct section *sec;
490 unsigned long off;
491 va_list args;
492
493 if (insn) {
494 sec = insn->sec;
495 off = insn->offset;
496 } else {
497 sec = NULL;
498 off = 0;
499 }
500
501 va_start(args, format);
502 disas_vprint(stream, sec, off, depth, format, args);
503 va_end(args);
504 }
505
506 /*
507 * Print an instruction address (offset and function), the instruction itself
508 * and an optional message.
509 */
disas_print_insn(FILE * stream,struct disas_context * dctx,struct instruction * insn,int depth,const char * format,...)510 void disas_print_insn(FILE *stream, struct disas_context *dctx,
511 struct instruction *insn, int depth,
512 const char *format, ...)
513 {
514 char fake_nop_insn[32];
515 const char *insn_str;
516 bool fake_nop;
517 va_list args;
518 int len;
519
520 /*
521 * Alternative can insert a fake nop, sometimes with no
522 * associated section so nothing to disassemble.
523 */
524 fake_nop = (!insn->sec && insn->type == INSN_NOP);
525 if (fake_nop) {
526 snprintf(fake_nop_insn, 32, "<fake nop> (%d bytes)", insn->len);
527 insn_str = fake_nop_insn;
528 } else {
529 disas_insn(dctx, insn);
530 insn_str = disas_result(dctx);
531 }
532
533 /* print the instruction */
534 len = (depth + 1) * 2 < DISAS_INSN_SPACE ? DISAS_INSN_SPACE - (depth+1) * 2 : 1;
535 disas_print_info(stream, insn, depth, "%-*s", len, insn_str);
536
537 /* print message if any */
538 if (!format)
539 return;
540
541 if (strcmp(format, "\n") == 0) {
542 fprintf(stream, "\n");
543 return;
544 }
545
546 fprintf(stream, " - ");
547 va_start(args, format);
548 vfprintf(stream, format, args);
549 va_end(args);
550 }
551
552 /*
553 * Disassemble a single instruction. Return the size of the instruction.
554 *
555 * If alt_applied is true then insn should be an instruction from of an
556 * alternative (i.e. insn->alt_group != NULL), and it is disassembled
557 * at the location of the original code it is replacing. When the
558 * instruction references any address inside the alternative then
559 * these references will be re-adjusted to replace the original code.
560 */
disas_insn_common(struct disas_context * dctx,struct instruction * insn,bool alt_applied)561 static size_t disas_insn_common(struct disas_context *dctx,
562 struct instruction *insn,
563 bool alt_applied)
564 {
565 disassembler_ftype disasm = dctx->disassembler;
566 struct disassemble_info *dinfo = &dctx->info;
567
568 dctx->insn = insn;
569 dctx->alt_applied = alt_applied;
570 dctx->result[0] = '\0';
571
572 if (insn->type == INSN_NOP) {
573 DINFO_FPRINTF(dinfo, "nop%d", insn->len);
574 return insn->len;
575 }
576
577 /*
578 * Set the disassembler buffer to read data from the section
579 * containing the instruction to disassemble.
580 */
581 dinfo->buffer = insn->sec->data->d_buf;
582 dinfo->buffer_vma = 0;
583 dinfo->buffer_length = insn->sec->sh.sh_size;
584
585 return disasm(insn->offset, &dctx->info);
586 }
587
disas_insn(struct disas_context * dctx,struct instruction * insn)588 size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
589 {
590 return disas_insn_common(dctx, insn, false);
591 }
592
disas_insn_alt(struct disas_context * dctx,struct instruction * insn)593 static size_t disas_insn_alt(struct disas_context *dctx,
594 struct instruction *insn)
595 {
596 return disas_insn_common(dctx, insn, true);
597 }
598
next_insn_same_alt(struct objtool_file * file,struct alt_group * alt_grp,struct instruction * insn)599 static struct instruction *next_insn_same_alt(struct objtool_file *file,
600 struct alt_group *alt_grp,
601 struct instruction *insn)
602 {
603 if (alt_grp->last_insn == insn || alt_grp->nop == insn)
604 return NULL;
605
606 return next_insn_same_sec(file, insn);
607 }
608
609 #define alt_for_each_insn(file, alt_grp, insn) \
610 for (insn = alt_grp->first_insn; \
611 insn; \
612 insn = next_insn_same_alt(file, alt_grp, insn))
613
614 /*
615 * Provide a name for the type of alternatives present at the
616 * specified instruction.
617 *
618 * An instruction can have alternatives with different types, for
619 * example alternative instructions and an exception table. In that
620 * case the name for the alternative instructions type is used.
621 *
622 * Return NULL if the instruction as no alternative.
623 */
disas_alt_type_name(struct instruction * insn)624 const char *disas_alt_type_name(struct instruction *insn)
625 {
626 struct alternative *alt;
627 const char *name;
628
629 name = NULL;
630 for (alt = insn->alts; alt; alt = alt->next) {
631 if (alt->type == ALT_TYPE_INSTRUCTIONS) {
632 name = "alternative";
633 break;
634 }
635
636 switch (alt->type) {
637 case ALT_TYPE_EX_TABLE:
638 name = "ex_table";
639 break;
640 case ALT_TYPE_JUMP_TABLE:
641 name = "jump_table";
642 break;
643 default:
644 name = "unknown";
645 break;
646 }
647 }
648
649 return name;
650 }
651
652 /*
653 * Provide a name for an alternative.
654 */
disas_alt_name(struct alternative * alt)655 char *disas_alt_name(struct alternative *alt)
656 {
657 char pfx[4] = { 0 };
658 char *str = NULL;
659 const char *name;
660 int feature;
661 int flags;
662 int num;
663
664 switch (alt->type) {
665
666 case ALT_TYPE_EX_TABLE:
667 str = strdup("EXCEPTION");
668 break;
669
670 case ALT_TYPE_JUMP_TABLE:
671 str = strdup("JUMP");
672 break;
673
674 case ALT_TYPE_INSTRUCTIONS:
675 /*
676 * This is a non-default group alternative. Create a name
677 * based on the feature and flags associated with this
678 * alternative. Use either the feature name (it is available)
679 * or the feature number. And add a prefix to show the flags
680 * used.
681 *
682 * Prefix flags characters:
683 *
684 * '!' alternative used when feature not enabled
685 * '+' direct call alternative
686 * '?' unknown flag
687 */
688
689 if (!alt->insn->alt_group)
690 return NULL;
691
692 feature = alt->insn->alt_group->feature;
693 num = alt_feature(feature);
694 flags = alt_flags(feature);
695 str = pfx;
696
697 if (flags & ~(ALT_FLAG_NOT | ALT_FLAG_DIRECT_CALL))
698 *str++ = '?';
699 if (flags & ALT_FLAG_DIRECT_CALL)
700 *str++ = '+';
701 if (flags & ALT_FLAG_NOT)
702 *str++ = '!';
703
704 name = arch_cpu_feature_name(num);
705 if (!name)
706 str = strfmt("%sFEATURE 0x%X", pfx, num);
707 else
708 str = strfmt("%s%s", pfx, name);
709
710 break;
711 }
712
713 return str;
714 }
715
716 /*
717 * Initialize an alternative. The default alternative should be initialized
718 * with alt=NULL.
719 */
disas_alt_init(struct disas_alt * dalt,struct instruction * orig_insn,struct alternative * alt)720 static int disas_alt_init(struct disas_alt *dalt,
721 struct instruction *orig_insn,
722 struct alternative *alt)
723 {
724 dalt->orig_insn = orig_insn;
725 dalt->alt = alt;
726 dalt->insn_idx = 0;
727 dalt->name = alt ? disas_alt_name(alt) : strdup("DEFAULT");
728 if (!dalt->name)
729 return -1;
730 dalt->width = strlen(dalt->name);
731
732 return 0;
733 }
734
disas_alt_add_insn(struct disas_alt * dalt,int index,char * insn_str,int offset,int nops)735 static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str,
736 int offset, int nops)
737 {
738 int len;
739
740 if (index >= DISAS_ALT_INSN_MAX) {
741 WARN("Alternative %lx.%s has more instructions than supported",
742 DALT_ALTID(dalt), dalt->name);
743 return -1;
744 }
745
746 len = strlen(insn_str);
747 dalt->insn[index].str = insn_str;
748 dalt->insn[index].offset = offset;
749 dalt->insn[index].nops = nops;
750 if (len > dalt->width)
751 dalt->width = len;
752
753 return 0;
754 }
755
disas_alt_jump(struct disas_alt * dalt)756 static int disas_alt_jump(struct disas_alt *dalt)
757 {
758 struct instruction *orig_insn;
759 struct instruction *dest_insn;
760 char suffix[2] = { 0 };
761 char *str;
762 int nops;
763
764 orig_insn = dalt->orig_insn;
765 dest_insn = dalt->alt->insn;
766
767 if (orig_insn->type == INSN_NOP) {
768 if (orig_insn->len == 5)
769 suffix[0] = 'q';
770 str = strfmt("jmp%-3s %lx <%s+0x%lx>", suffix,
771 dest_insn->offset, dest_insn->sym->name,
772 dest_insn->offset - dest_insn->sym->offset);
773 nops = 0;
774 } else {
775 str = strfmt("nop%d", orig_insn->len);
776 nops = orig_insn->len;
777 }
778
779 if (!str)
780 return -1;
781
782 disas_alt_add_insn(dalt, 0, str, 0, nops);
783
784 return 1;
785 }
786
787 /*
788 * Disassemble an exception table alternative.
789 */
disas_alt_extable(struct disas_alt * dalt)790 static int disas_alt_extable(struct disas_alt *dalt)
791 {
792 struct instruction *alt_insn;
793 char *str;
794
795 alt_insn = dalt->alt->insn;
796 str = strfmt("resume at 0x%lx <%s+0x%lx>",
797 alt_insn->offset, alt_insn->sym->name,
798 alt_insn->offset - alt_insn->sym->offset);
799 if (!str)
800 return -1;
801
802 disas_alt_add_insn(dalt, 0, str, 0, 0);
803
804 return 1;
805 }
806
807 /*
808 * Disassemble an alternative and store instructions in the disas_alt
809 * structure. Return the number of instructions in the alternative.
810 */
disas_alt_group(struct disas_context * dctx,struct disas_alt * dalt)811 static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
812 {
813 struct objtool_file *file;
814 struct instruction *insn;
815 int offset;
816 char *str;
817 int count;
818 int nops;
819 int err;
820
821 file = dctx->file;
822 count = 0;
823 offset = 0;
824 nops = 0;
825
826 alt_for_each_insn(file, DALT_GROUP(dalt), insn) {
827
828 disas_insn_alt(dctx, insn);
829 str = strdup(disas_result(dctx));
830 if (!str)
831 return -1;
832
833 nops = insn->type == INSN_NOP ? insn->len : 0;
834 err = disas_alt_add_insn(dalt, count, str, offset, nops);
835 if (err)
836 break;
837 offset += insn->len;
838 count++;
839 }
840
841 return count;
842 }
843
844 /*
845 * Disassemble the default alternative.
846 */
disas_alt_default(struct disas_context * dctx,struct disas_alt * dalt)847 static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
848 {
849 char *str;
850 int nops;
851 int err;
852
853 if (DALT_GROUP(dalt))
854 return disas_alt_group(dctx, dalt);
855
856 /*
857 * Default alternative with no alt_group: this is the default
858 * code associated with either a jump table or an exception
859 * table and no other instruction alternatives. In that case
860 * the default alternative is made of a single instruction.
861 */
862 disas_insn(dctx, dalt->orig_insn);
863 str = strdup(disas_result(dctx));
864 if (!str)
865 return -1;
866 nops = dalt->orig_insn->type == INSN_NOP ? dalt->orig_insn->len : 0;
867 err = disas_alt_add_insn(dalt, 0, str, 0, nops);
868 if (err)
869 return -1;
870
871 return 1;
872 }
873
874 /*
875 * For each alternative, if there is an instruction at the specified
876 * offset then print this instruction, otherwise print a blank entry.
877 * The offset is an offset from the start of the alternative.
878 *
879 * Return the offset for the next instructions to print, or -1 if all
880 * instructions have been printed.
881 */
disas_alt_print_insn(struct disas_alt * dalts,int alt_count,int insn_count,int offset)882 static int disas_alt_print_insn(struct disas_alt *dalts, int alt_count,
883 int insn_count, int offset)
884 {
885 struct disas_alt *dalt;
886 int offset_next;
887 char *str;
888 int i, j;
889
890 offset_next = -1;
891
892 for (i = 0; i < alt_count; i++) {
893 dalt = &dalts[i];
894 j = dalt->insn_idx;
895 if (j == -1) {
896 printf("| %-*s ", dalt->width, "");
897 continue;
898 }
899
900 if (dalt->insn[j].offset == offset) {
901 str = dalt->insn[j].str;
902 printf("| %-*s ", dalt->width, str ?: "");
903 if (++j < insn_count) {
904 dalt->insn_idx = j;
905 } else {
906 dalt->insn_idx = -1;
907 continue;
908 }
909 } else {
910 printf("| %-*s ", dalt->width, "");
911 }
912
913 if (dalt->insn[j].offset > 0 &&
914 (offset_next == -1 ||
915 (dalt->insn[j].offset < offset_next)))
916 offset_next = dalt->insn[j].offset;
917 }
918 printf("\n");
919
920 return offset_next;
921 }
922
923 /*
924 * Print all alternatives side-by-side.
925 */
disas_alt_print_wide(char * alt_name,struct disas_alt * dalts,int alt_count,int insn_count)926 static void disas_alt_print_wide(char *alt_name, struct disas_alt *dalts, int alt_count,
927 int insn_count)
928 {
929 struct instruction *orig_insn;
930 int offset_next;
931 int offset;
932 int i;
933
934 orig_insn = dalts[0].orig_insn;
935
936 /*
937 * Print an header with the name of each alternative.
938 */
939 disas_print_info(stdout, orig_insn, -2, NULL);
940
941 if (strlen(alt_name) > dalts[0].width)
942 dalts[0].width = strlen(alt_name);
943 printf("| %-*s ", dalts[0].width, alt_name);
944
945 for (i = 1; i < alt_count; i++)
946 printf("| %-*s ", dalts[i].width, dalts[i].name);
947
948 printf("\n");
949
950 /*
951 * Print instructions for each alternative.
952 */
953 offset_next = 0;
954 do {
955 offset = offset_next;
956 disas_print(stdout, orig_insn->sec, orig_insn->offset + offset,
957 -2, NULL);
958 offset_next = disas_alt_print_insn(dalts, alt_count, insn_count,
959 offset);
960 } while (offset_next > offset);
961 }
962
963 /*
964 * Print all alternatives one above the other.
965 */
disas_alt_print_compact(char * alt_name,struct disas_alt * dalts,int alt_count,int insn_count)966 static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
967 int alt_count, int insn_count)
968 {
969 struct instruction *orig_insn;
970 int width;
971 int i, j;
972 int len;
973
974 orig_insn = dalts[0].orig_insn;
975
976 len = disas_print(stdout, orig_insn->sec, orig_insn->offset, 0, NULL);
977 printf("%s\n", alt_name);
978
979 /*
980 * If all alternatives have a single instruction then print each
981 * alternative on a single line. Otherwise, print alternatives
982 * one above the other with a clear separation.
983 */
984
985 if (insn_count == 1) {
986 width = 0;
987 for (i = 0; i < alt_count; i++) {
988 if (dalts[i].width > width)
989 width = dalts[i].width;
990 }
991
992 for (i = 0; i < alt_count; i++) {
993 printf("%*s= %-*s (if %s)\n", len, "", width,
994 dalts[i].insn[0].str, dalts[i].name);
995 }
996
997 return;
998 }
999
1000 for (i = 0; i < alt_count; i++) {
1001 printf("%*s= %s\n", len, "", dalts[i].name);
1002 for (j = 0; j < insn_count; j++) {
1003 if (!dalts[i].insn[j].str)
1004 break;
1005 disas_print(stdout, orig_insn->sec,
1006 orig_insn->offset + dalts[i].insn[j].offset, 0,
1007 "| %s\n", dalts[i].insn[j].str);
1008 }
1009 printf("%*s|\n", len, "");
1010 }
1011 }
1012
1013 /*
1014 * Trim NOPs in alternatives. This replaces trailing NOPs in alternatives
1015 * with a single indication of the number of bytes covered with NOPs.
1016 *
1017 * Return the maximum numbers of instructions in all alternatives after
1018 * trailing NOPs have been trimmed.
1019 */
disas_alt_trim_nops(struct disas_alt * dalts,int alt_count,int insn_count)1020 static int disas_alt_trim_nops(struct disas_alt *dalts, int alt_count,
1021 int insn_count)
1022 {
1023 struct disas_alt *dalt;
1024 int nops_count;
1025 const char *s;
1026 int offset;
1027 int count;
1028 int nops;
1029 int i, j;
1030
1031 count = 0;
1032 for (i = 0; i < alt_count; i++) {
1033 offset = 0;
1034 nops = 0;
1035 nops_count = 0;
1036 dalt = &dalts[i];
1037 for (j = insn_count - 1; j >= 0; j--) {
1038 if (!dalt->insn[j].str || !dalt->insn[j].nops)
1039 break;
1040 offset = dalt->insn[j].offset;
1041 free(dalt->insn[j].str);
1042 dalt->insn[j].offset = 0;
1043 dalt->insn[j].str = NULL;
1044 nops += dalt->insn[j].nops;
1045 nops_count++;
1046 }
1047
1048 /*
1049 * All trailing NOPs have been removed. If there was a single
1050 * NOP instruction then re-add it. If there was a block of
1051 * NOPs then indicate the number of bytes than the block
1052 * covers (nop*<number-of-bytes>).
1053 */
1054 if (nops_count) {
1055 s = nops_count == 1 ? "" : "*";
1056 dalt->insn[j + 1].str = strfmt("nop%s%d", s, nops);
1057 dalt->insn[j + 1].offset = offset;
1058 dalt->insn[j + 1].nops = nops;
1059 j++;
1060 }
1061
1062 if (j > count)
1063 count = j;
1064 }
1065
1066 return count + 1;
1067 }
1068
1069 /*
1070 * Disassemble an alternative.
1071 *
1072 * Return the last instruction in the default alternative so that
1073 * disassembly can continue with the next instruction. Return NULL
1074 * on error.
1075 */
disas_alt(struct disas_context * dctx,struct instruction * orig_insn)1076 static void *disas_alt(struct disas_context *dctx,
1077 struct instruction *orig_insn)
1078 {
1079 struct disas_alt dalts[DISAS_ALT_MAX] = { 0 };
1080 struct instruction *last_insn = NULL;
1081 struct alternative *alt;
1082 struct disas_alt *dalt;
1083 int insn_count = 0;
1084 int alt_count = 0;
1085 char *alt_name;
1086 int count;
1087 int i, j;
1088 int err;
1089
1090 alt_name = strfmt("<%s.%lx>", disas_alt_type_name(orig_insn),
1091 orig_insn->offset);
1092 if (!alt_name) {
1093 WARN("Failed to define name for alternative at instruction 0x%lx",
1094 orig_insn->offset);
1095 goto done;
1096 }
1097
1098 /*
1099 * Initialize and disassemble the default alternative.
1100 */
1101 err = disas_alt_init(&dalts[0], orig_insn, NULL);
1102 if (err) {
1103 WARN("%s: failed to initialize default alternative", alt_name);
1104 goto done;
1105 }
1106
1107 insn_count = disas_alt_default(dctx, &dalts[0]);
1108 if (insn_count < 0) {
1109 WARN("%s: failed to disassemble default alternative", alt_name);
1110 goto done;
1111 }
1112
1113 /*
1114 * Initialize and disassemble all other alternatives.
1115 */
1116 i = 1;
1117 for (alt = orig_insn->alts; alt; alt = alt->next) {
1118 if (i >= DISAS_ALT_MAX) {
1119 WARN("%s has more alternatives than supported", alt_name);
1120 break;
1121 }
1122
1123 dalt = &dalts[i];
1124 err = disas_alt_init(dalt, orig_insn, alt);
1125 if (err) {
1126 WARN("%s: failed to disassemble alternative", alt_name);
1127 goto done;
1128 }
1129
1130 count = -1;
1131 switch (dalt->alt->type) {
1132 case ALT_TYPE_INSTRUCTIONS:
1133 count = disas_alt_group(dctx, dalt);
1134 break;
1135 case ALT_TYPE_EX_TABLE:
1136 count = disas_alt_extable(dalt);
1137 break;
1138 case ALT_TYPE_JUMP_TABLE:
1139 count = disas_alt_jump(dalt);
1140 break;
1141 }
1142 if (count < 0) {
1143 WARN("%s: failed to disassemble alternative %s",
1144 alt_name, dalt->name);
1145 goto done;
1146 }
1147
1148 insn_count = count > insn_count ? count : insn_count;
1149 i++;
1150 }
1151 alt_count = i;
1152
1153 /*
1154 * Print default and non-default alternatives.
1155 */
1156
1157 insn_count = disas_alt_trim_nops(dalts, alt_count, insn_count);
1158
1159 if (opts.wide)
1160 disas_alt_print_wide(alt_name, dalts, alt_count, insn_count);
1161 else
1162 disas_alt_print_compact(alt_name, dalts, alt_count, insn_count);
1163
1164 last_insn = orig_insn->alt_group ? orig_insn->alt_group->last_insn :
1165 orig_insn;
1166
1167 done:
1168 for (i = 0; i < alt_count; i++) {
1169 free(dalts[i].name);
1170 for (j = 0; j < insn_count; j++)
1171 free(dalts[i].insn[j].str);
1172 }
1173
1174 free(alt_name);
1175
1176 return last_insn;
1177 }
1178
1179 /*
1180 * Disassemble a function.
1181 */
disas_func(struct disas_context * dctx,struct symbol * func)1182 static void disas_func(struct disas_context *dctx, struct symbol *func)
1183 {
1184 struct instruction *insn_start;
1185 struct instruction *insn;
1186
1187 printf("%s:\n", func->name);
1188 sym_for_each_insn(dctx->file, func, insn) {
1189 if (insn->alts) {
1190 insn_start = insn;
1191 insn = disas_alt(dctx, insn);
1192 if (insn)
1193 continue;
1194 /*
1195 * There was an error with disassembling
1196 * the alternative. Resume disassembling
1197 * at the current instruction, this will
1198 * disassemble the default alternative
1199 * only and continue with the code after
1200 * the alternative.
1201 */
1202 insn = insn_start;
1203 }
1204
1205 DISAS_PRINSN(dctx, insn, 0);
1206 }
1207 printf("\n");
1208 }
1209
1210 /*
1211 * Disassemble all warned functions.
1212 */
disas_warned_funcs(struct disas_context * dctx)1213 void disas_warned_funcs(struct disas_context *dctx)
1214 {
1215 struct symbol *sym;
1216
1217 if (!dctx)
1218 return;
1219
1220 for_each_sym(dctx->file->elf, sym) {
1221 if (sym->warned)
1222 disas_func(dctx, sym);
1223 }
1224 }
1225
disas_funcs(struct disas_context * dctx)1226 void disas_funcs(struct disas_context *dctx)
1227 {
1228 bool disas_all = !strcmp(opts.disas, "*");
1229 struct section *sec;
1230 struct symbol *sym;
1231
1232 for_each_sec(dctx->file->elf, sec) {
1233
1234 if (!(sec->sh.sh_flags & SHF_EXECINSTR))
1235 continue;
1236
1237 sec_for_each_sym(sec, sym) {
1238 /*
1239 * If the function had a warning and the verbose
1240 * option is used then the function was already
1241 * disassemble.
1242 */
1243 if (opts.verbose && sym->warned)
1244 continue;
1245
1246 if (disas_all || fnmatch(opts.disas, sym->name, 0) == 0)
1247 disas_func(dctx, sym);
1248 }
1249 }
1250 }
1251