File Index
Files and directories
Sign in
FILE

disas.c

File preview with syntax highlighting.

linux-6.19.14/tools/objtool/disas.c C
   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
  25struct 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 */
  47struct 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
  70static int alt_feature(unsigned int ft_flags)
  71{
  72	return (ft_flags & ALT_FEATURE_MASK);
  73}
  74
  75static 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 */
  84static 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
  97static 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
 114static 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
 138static 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 */
 153static 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
 167static 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
 183static 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
 220static 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
 238static 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 */
 280static 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 */
 329int 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
 354struct 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
 407error:
 408	free(dctx);
 409	return NULL;
 410}
 411
 412void disas_context_destroy(struct disas_context *dctx)
 413{
 414	free(dctx);
 415}
 416
 417char *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 */
 433static 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
 467static 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 */
 486void 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 */
 510void 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 */
 561static 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
 588size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
 589{
 590	return disas_insn_common(dctx, insn, false);
 591}
 592
 593static size_t disas_insn_alt(struct disas_context *dctx,
 594			     struct instruction *insn)
 595{
 596	return disas_insn_common(dctx, insn, true);
 597}
 598
 599static 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 */
 624const 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 */
 655char *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 */
 720static 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
 735static 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
 756static 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 */
 790static 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 */
 811static 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 */
 847static 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 */
 882static 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 */
 926static 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 */
 966static 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 */
1020static 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 */
1076static 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
1167done:
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 */
1182static 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 */
1213void 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
1226void 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}
~/ Read-only file preview.