3228 lines · 78.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> * * Parts came from builtin-annotate.c, see those files for further * copyright notes. */ #include <errno.h>#include <inttypes.h>#include <libgen.h>#include <stdlib.h>#include "util.h" // hex_width()#include "ui/ui.h"#include "sort.h"#include "build-id.h"#include "color.h"#include "config.h"#include "dso.h"#include "env.h"#include "map.h"#include "maps.h"#include "symbol.h"#include "srcline.h"#include "units.h"#include "debug.h"#include "annotate.h"#include "evsel.h"#include "evlist.h"#include "bpf-event.h"#include "block-range.h"#include "string2.h"#include "util/event.h"#include "arch/common.h"#include <regex.h>#include <pthread.h>#include <linux/bitops.h>#include <linux/kernel.h>#include <linux/string.h>#include <subcmd/parse-options.h>#include <subcmd/run-command.h> /* FIXME: For the HE_COLORSET */#include "ui/browser.h" /* * FIXME: Using the same values as slang.h, * but that header may not be available everywhere */#define LARROW_CHAR	((unsigned char)',')#define RARROW_CHAR	((unsigned char)'+')#define DARROW_CHAR	((unsigned char)'.')#define UARROW_CHAR	((unsigned char)'-') #include <linux/ctype.h> struct annotation_options annotation__default_options = {	.use_offset     = true,	.jump_arrows    = true,	.annotate_src	= true,	.offset_level	= ANNOTATION__OFFSET_JUMP_TARGETS,	.percent_type	= PERCENT_PERIOD_LOCAL,}; static regex_t	 file_lineno; static struct ins_ops *ins__find(struct arch *arch, const char *name);static void ins__sort(struct arch *arch);static int disasm_line__parse(char *line, const char **namep, char **rawp); struct arch {	const char	*name;	struct ins	*instructions;	size_t		nr_instructions;	size_t		nr_instructions_allocated;	struct ins_ops  *(*associate_instruction_ops)(struct arch *arch, const char *name);	bool		sorted_instructions;	bool		initialized;	void		*priv;	unsigned int	model;	unsigned int	family;	int		(*init)(struct arch *arch, char *cpuid);	bool		(*ins_is_fused)(struct arch *arch, const char *ins1,					const char *ins2);	struct		{		char comment_char;		char skip_functions_char;	} objdump;}; static struct ins_ops call_ops;static struct ins_ops dec_ops;static struct ins_ops jump_ops;static struct ins_ops mov_ops;static struct ins_ops nop_ops;static struct ins_ops lock_ops;static struct ins_ops ret_ops; static int arch__grow_instructions(struct arch *arch){	struct ins *new_instructions;	size_t new_nr_allocated; 	if (arch->nr_instructions_allocated == 0 && arch->instructions)		goto grow_from_non_allocated_table; 	new_nr_allocated = arch->nr_instructions_allocated + 128;	new_instructions = realloc(arch->instructions, new_nr_allocated * sizeof(struct ins));	if (new_instructions == NULL)		return -1; out_update_instructions:	arch->instructions = new_instructions;	arch->nr_instructions_allocated = new_nr_allocated;	return 0; grow_from_non_allocated_table:	new_nr_allocated = arch->nr_instructions + 128;	new_instructions = calloc(new_nr_allocated, sizeof(struct ins));	if (new_instructions == NULL)		return -1; 	memcpy(new_instructions, arch->instructions, arch->nr_instructions);	goto out_update_instructions;} static int arch__associate_ins_ops(struct arch* arch, const char *name, struct ins_ops *ops){	struct ins *ins; 	if (arch->nr_instructions == arch->nr_instructions_allocated &&	    arch__grow_instructions(arch))		return -1; 	ins = &arch->instructions[arch->nr_instructions];	ins->name = strdup(name);	if (!ins->name)		return -1; 	ins->ops  = ops;	arch->nr_instructions++; 	ins__sort(arch);	return 0;} #include "arch/arc/annotate/instructions.c"#include "arch/arm/annotate/instructions.c"#include "arch/arm64/annotate/instructions.c"#include "arch/csky/annotate/instructions.c"#include "arch/mips/annotate/instructions.c"#include "arch/x86/annotate/instructions.c"#include "arch/powerpc/annotate/instructions.c"#include "arch/s390/annotate/instructions.c"#include "arch/sparc/annotate/instructions.c" static struct arch architectures[] = {	{		.name = "arc",		.init = arc__annotate_init,	},	{		.name = "arm",		.init = arm__annotate_init,	},	{		.name = "arm64",		.init = arm64__annotate_init,	},	{		.name = "csky",		.init = csky__annotate_init,	},	{		.name = "mips",		.init = mips__annotate_init,		.objdump = {			.comment_char = '#',		},	},	{		.name = "x86",		.init = x86__annotate_init,		.instructions = x86__instructions,		.nr_instructions = ARRAY_SIZE(x86__instructions),		.ins_is_fused = x86__ins_is_fused,		.objdump =  {			.comment_char = '#',		},	},	{		.name = "powerpc",		.init = powerpc__annotate_init,	},	{		.name = "s390",		.init = s390__annotate_init,		.objdump =  {			.comment_char = '#',		},	},	{		.name = "sparc",		.init = sparc__annotate_init,		.objdump = {			.comment_char = '#',		},	},}; static void ins__delete(struct ins_operands *ops){	if (ops == NULL)		return;	zfree(&ops->source.raw);	zfree(&ops->source.name);	zfree(&ops->target.raw);	zfree(&ops->target.name);} static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size,			      struct ins_operands *ops, int max_ins_name){	return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->raw);} int ins__scnprintf(struct ins *ins, char *bf, size_t size,		   struct ins_operands *ops, int max_ins_name){	if (ins->ops->scnprintf)		return ins->ops->scnprintf(ins, bf, size, ops, max_ins_name); 	return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name);} bool ins__is_fused(struct arch *arch, const char *ins1, const char *ins2){	if (!arch || !arch->ins_is_fused)		return false; 	return arch->ins_is_fused(arch, ins1, ins2);} static int call__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms){	char *endptr, *tok, *name;	struct map *map = ms->map;	struct addr_map_symbol target = {		.ms = { .map = map, },	}; 	ops->target.addr = strtoull(ops->raw, &endptr, 16); 	name = strchr(endptr, '<');	if (name == NULL)		goto indirect_call; 	name++; 	if (arch->objdump.skip_functions_char &&	    strchr(name, arch->objdump.skip_functions_char))		return -1; 	tok = strchr(name, '>');	if (tok == NULL)		return -1; 	*tok = '\0';	ops->target.name = strdup(name);	*tok = '>'; 	if (ops->target.name == NULL)		return -1;find_target:	target.addr = map__objdump_2mem(map, ops->target.addr); 	if (maps__find_ams(ms->maps, &target) == 0 &&	    map__rip_2objdump(target.ms.map, map->map_ip(target.ms.map, target.addr)) == ops->target.addr)		ops->target.sym = target.ms.sym; 	return 0; indirect_call:	tok = strchr(endptr, '*');	if (tok != NULL) {		endptr++; 		/* Indirect call can use a non-rip register and offset: callq  *0x8(%rbx).		 * Do not parse such instruction.  */		if (strstr(endptr, "(%r") == NULL)			ops->target.addr = strtoull(endptr, NULL, 16);	}	goto find_target;} static int call__scnprintf(struct ins *ins, char *bf, size_t size,			   struct ins_operands *ops, int max_ins_name){	if (ops->target.sym)		return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.sym->name); 	if (ops->target.addr == 0)		return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name); 	if (ops->target.name)		return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.name); 	return scnprintf(bf, size, "%-*s *%" PRIx64, max_ins_name, ins->name, ops->target.addr);} static struct ins_ops call_ops = {	.parse	   = call__parse,	.scnprintf = call__scnprintf,}; bool ins__is_call(const struct ins *ins){	return ins->ops == &call_ops || ins->ops == &s390_call_ops;} /* * Prevents from matching commas in the comment section, e.g.: * ffff200008446e70:       b.cs    ffff2000084470f4 <generic_exec_single+0x314>  // b.hs, b.nlast * * and skip comma as part of function arguments, e.g.: * 1d8b4ac <linemap_lookup(line_maps const*, unsigned int)+0xcc> */static inline const char *validate_comma(const char *c, struct ins_operands *ops){	if (ops->raw_comment && c > ops->raw_comment)		return NULL; 	if (ops->raw_func_start && c > ops->raw_func_start)		return NULL; 	return c;} static int jump__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms){	struct map *map = ms->map;	struct symbol *sym = ms->sym;	struct addr_map_symbol target = {		.ms = { .map = map, },	};	const char *c = strchr(ops->raw, ',');	u64 start, end; 	ops->raw_comment = strchr(ops->raw, arch->objdump.comment_char);	ops->raw_func_start = strchr(ops->raw, '<'); 	c = validate_comma(c, ops); 	/*	 * Examples of lines to parse for the _cpp_lex_token@@Base	 * function:	 *	 * 1159e6c: jne    115aa32 <_cpp_lex_token@@Base+0xf92>	 * 1159e8b: jne    c469be <cpp_named_operator2name@@Base+0xa72>	 *	 * The first is a jump to an offset inside the same function,	 * the second is to another function, i.e. that 0xa72 is an	 * offset in the cpp_named_operator2name@@base function.	 */	/*	 * skip over possible up to 2 operands to get to address, e.g.:	 * tbnz	 w0, #26, ffff0000083cd190 <security_file_permission+0xd0>	 */	if (c++ != NULL) {		ops->target.addr = strtoull(c, NULL, 16);		if (!ops->target.addr) {			c = strchr(c, ',');			c = validate_comma(c, ops);			if (c++ != NULL)				ops->target.addr = strtoull(c, NULL, 16);		}	} else {		ops->target.addr = strtoull(ops->raw, NULL, 16);	} 	target.addr = map__objdump_2mem(map, ops->target.addr);	start = map->unmap_ip(map, sym->start),	end = map->unmap_ip(map, sym->end); 	ops->target.outside = target.addr < start || target.addr > end; 	/*	 * FIXME: things like this in _cpp_lex_token (gcc's cc1 program): 		cpp_named_operator2name@@Base+0xa72 	 * Point to a place that is after the cpp_named_operator2name	 * boundaries, i.e.  in the ELF symbol table for cc1	 * cpp_named_operator2name is marked as being 32-bytes long, but it in	 * fact is much larger than that, so we seem to need a symbols__find()	 * routine that looks for >= current->start and  < next_symbol->start,	 * possibly just for C++ objects?	 *	 * For now lets just make some progress by marking jumps to outside the	 * current function as call like.	 *	 * Actual navigation will come next, with further understanding of how	 * the symbol searching and disassembly should be done.	 */	if (maps__find_ams(ms->maps, &target) == 0 &&	    map__rip_2objdump(target.ms.map, map->map_ip(target.ms.map, target.addr)) == ops->target.addr)		ops->target.sym = target.ms.sym; 	if (!ops->target.outside) {		ops->target.offset = target.addr - start;		ops->target.offset_avail = true;	} else {		ops->target.offset_avail = false;	} 	return 0;} static int jump__scnprintf(struct ins *ins, char *bf, size_t size,			   struct ins_operands *ops, int max_ins_name){	const char *c; 	if (!ops->target.addr || ops->target.offset < 0)		return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name); 	if (ops->target.outside && ops->target.sym != NULL)		return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.sym->name); 	c = strchr(ops->raw, ',');	c = validate_comma(c, ops); 	if (c != NULL) {		const char *c2 = strchr(c + 1, ','); 		c2 = validate_comma(c2, ops);		/* check for 3-op insn */		if (c2 != NULL)			c = c2;		c++; 		/* mirror arch objdump's space-after-comma style */		if (*c == ' ')			c++;	} 	return scnprintf(bf, size, "%-*s %.*s%" PRIx64, max_ins_name,			 ins->name, c ? c - ops->raw : 0, ops->raw,			 ops->target.offset);} static struct ins_ops jump_ops = {	.parse	   = jump__parse,	.scnprintf = jump__scnprintf,}; bool ins__is_jump(const struct ins *ins){	return ins->ops == &jump_ops;} static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep){	char *endptr, *name, *t; 	if (strstr(raw, "(%rip)") == NULL)		return 0; 	*addrp = strtoull(comment, &endptr, 16);	if (endptr == comment)		return 0;	name = strchr(endptr, '<');	if (name == NULL)		return -1; 	name++; 	t = strchr(name, '>');	if (t == NULL)		return 0; 	*t = '\0';	*namep = strdup(name);	*t = '>'; 	return 0;} static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms){	ops->locked.ops = zalloc(sizeof(*ops->locked.ops));	if (ops->locked.ops == NULL)		return 0; 	if (disasm_line__parse(ops->raw, &ops->locked.ins.name, &ops->locked.ops->raw) < 0)		goto out_free_ops; 	ops->locked.ins.ops = ins__find(arch, ops->locked.ins.name); 	if (ops->locked.ins.ops == NULL)

Lines 1–500 of 3228.