1648 lines · 41.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only#include <ctype.h>#include <elf.h>#ifndef EF_CSKY_ABIMASK#define EF_CSKY_ABIMASK	0XF0000000#endif#include <errno.h>#include <fcntl.h>#include <inttypes.h>#include <libgen.h>#include <regex.h>#include <stdlib.h>#include <unistd.h> #include <linux/string.h>#include <linux/zalloc.h>#include <subcmd/run-command.h> #include "annotate.h"#include "annotate-data.h"#include "build-id.h"#include "capstone.h"#include "debug.h"#include "disasm.h"#include "dso.h"#include "dwarf-regs.h"#include "env.h"#include "evsel.h"#include "libbfd.h"#include "llvm.h"#include "map.h"#include "maps.h"#include "namespaces.h"#include "srcline.h"#include "symbol.h"#include "thread.h"#include "util.h" static regex_t	 file_lineno; /* These can be referred from the arch-dependent code */const struct ins_ops call_ops;const struct ins_ops dec_ops;const struct ins_ops jump_ops;const struct ins_ops mov_ops;const struct ins_ops nop_ops;const struct ins_ops lock_ops;const struct ins_ops ret_ops;const struct ins_ops load_store_ops;const struct ins_ops arithmetic_ops; static void ins__sort(struct arch *arch);static int disasm_line__parse(char *line, const char **namep, char **rawp);static int disasm_line__parse_powerpc(struct disasm_line *dl, struct annotate_args *args); static __attribute__((constructor)) void symbol__init_regexpr(void){	regcomp(&file_lineno, "^/[^:]+:([0-9]+)", REG_EXTENDED);} 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((void *)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 * sizeof(struct ins));	goto out_update_instructions;} int arch__associate_ins_ops(struct arch *arch, const char *name, const struct ins_ops *ops){	struct ins *ins; 	if (arch->nr_instructions == arch->nr_instructions_allocated &&	    arch__grow_instructions(arch))		return -1; 	ins = (struct 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;} static int e_machine_and_eflags__cmp(const struct e_machine_and_e_flags *val1,				     const struct e_machine_and_e_flags *val2){	if (val1->e_machine == val2->e_machine) {		if (val1->e_machine != EM_CSKY)			return 0;		if ((val1->e_flags & EF_CSKY_ABIMASK) < (val2->e_flags & EF_CSKY_ABIMASK))			return -1;		return (val1->e_flags & EF_CSKY_ABIMASK) > (val2->e_flags & EF_CSKY_ABIMASK);	}	return val1->e_machine < val2->e_machine ? -1 : 1;} static int arch__key_cmp(const void *key, const void *archp){	const struct arch *const *arch = archp; 	return e_machine_and_eflags__cmp(key, &(*arch)->id);} static int arch__cmp(const void *a, const void *b){	const struct arch *const *aa = a;	const struct arch *const *ab = b; 	return e_machine_and_eflags__cmp(&(*aa)->id, &(*ab)->id);} const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *cpuid){	static const struct arch *(*const arch_new_fn[])(const struct e_machine_and_e_flags *id,							 const char *cpuid) = {		[EM_386]	= arch__new_x86,		[EM_ARC]	= arch__new_arc,		[EM_ARM]	= arch__new_arm,		[EM_AARCH64]	= arch__new_arm64,		[EM_CSKY]	= arch__new_csky,		[EM_LOONGARCH]	= arch__new_loongarch,		[EM_MIPS]	= arch__new_mips,		[EM_PPC64]	= arch__new_powerpc,		[EM_PPC]	= arch__new_powerpc,		[EM_RISCV]	= arch__new_riscv64,		[EM_S390]	= arch__new_s390,		[EM_SPARC]	= arch__new_sparc,		[EM_SPARCV9]	= arch__new_sparc,		[EM_X86_64]	= arch__new_x86,	};	static const struct arch **archs;	static size_t num_archs;	struct e_machine_and_e_flags key = {		.e_machine = e_machine,		.e_flags = e_flags,	};	const struct arch *result = NULL, **tmp; 	if (num_archs > 0) {		tmp = bsearch(&key, archs, num_archs, sizeof(*archs), arch__key_cmp);		if (tmp)			result = *tmp;	} 	if (result)		return result; 	if (e_machine >= ARRAY_SIZE(arch_new_fn) || arch_new_fn[e_machine] == NULL) {		errno = ENOTSUP;		return NULL;	} 	tmp = reallocarray(archs, num_archs + 1, sizeof(*archs));	if (!tmp)		return NULL; 	archs = tmp; 	result = arch_new_fn[e_machine](&key, cpuid);	if (!result) {		pr_err("%s: failed to initialize %u arch priv area\n",			__func__, e_machine);		return NULL;	}	archs[num_archs++] = result;	qsort(archs, num_archs, sizeof(*archs), arch__cmp);	return result;} bool arch__is_x86(const struct arch *arch){	return arch->id.e_machine == EM_386 || arch->id.e_machine == EM_X86_64;} bool arch__is_powerpc(const struct arch *arch){	return arch->id.e_machine == EM_PPC || arch->id.e_machine == EM_PPC64;} static void ins_ops__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);} int ins__raw_scnprintf(const 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(const 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(const 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(const struct arch *arch, struct ins_operands *ops, struct map_symbol *ms,		struct disasm_line *dl __maybe_unused){	char *endptr, *tok, *name;	struct map *map = ms->map;	struct addr_map_symbol target; 	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 = (struct addr_map_symbol) {		.ms = { .map = map__get(map), },		.addr = map__objdump_2mem(map, ops->target.addr),	}; 	if (maps__find_ams(thread__maps(ms->thread), &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; 	addr_map_symbol__exit(&target);	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;} int call__scnprintf(const 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);} const struct ins_ops call_ops = {	.parse	   = call__parse,	.scnprintf = call__scnprintf,	.is_call   = true,}; bool ins__is_call(const struct ins *ins){	return ins->ops && ins->ops->is_call;} /* * 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->jump.raw_comment && c > ops->jump.raw_comment)		return NULL; 	if (ops->jump.raw_func_start && c > ops->jump.raw_func_start)		return NULL; 	return c;} static int jump__parse(const struct arch *arch, struct ins_operands *ops, struct map_symbol *ms,		struct disasm_line *dl __maybe_unused){	struct map *map = ms->map;	struct symbol *sym = ms->sym;	struct addr_map_symbol target = {		.ms = { .map = map__get(map), },	};	const char *c = strchr(ops->raw, ',');	u64 start, end; 	ops->jump.raw_comment = strchr(ops->raw, arch->objdump.comment_char);	ops->jump.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) {		c++;		ops->target.addr = strtoull(c, NULL, 16);		if (!ops->target.addr) {			c = strchr(c, ',');			c = validate_comma(c, ops);			if (c != NULL) {				c++;				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(thread__maps(ms->thread), &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;	}	addr_map_symbol__exit(&target);	return 0;} int jump__scnprintf(const 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);} void jump__delete(struct ins_operands *ops __maybe_unused){	/*	 * The ops->jump.raw_comment and ops->jump.raw_func_start belong to the	 * raw string, don't free them.	 */} const struct ins_ops jump_ops = {	.free	   = jump__delete,	.parse	   = jump__parse,	.scnprintf = jump__scnprintf,	.is_jump   = true,}; bool ins__is_jump(const struct ins *ins){	return ins->ops && ins->ops->is_jump;} 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;}

Lines 1–500 of 1648.