162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/compiler.h>
362306a36Sopenharmony_ci#include <sys/types.h>
462306a36Sopenharmony_ci#include <regex.h>
562306a36Sopenharmony_ci#include <stdlib.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_cistruct arm64_annotate {
862306a36Sopenharmony_ci	regex_t call_insn,
962306a36Sopenharmony_ci		jump_insn;
1062306a36Sopenharmony_ci};
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic int arm64_mov__parse(struct arch *arch __maybe_unused,
1362306a36Sopenharmony_ci			    struct ins_operands *ops,
1462306a36Sopenharmony_ci			    struct map_symbol *ms __maybe_unused)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	char *s = strchr(ops->raw, ','), *target, *endptr;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	if (s == NULL)
1962306a36Sopenharmony_ci		return -1;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	*s = '\0';
2262306a36Sopenharmony_ci	ops->source.raw = strdup(ops->raw);
2362306a36Sopenharmony_ci	*s = ',';
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	if (ops->source.raw == NULL)
2662306a36Sopenharmony_ci		return -1;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	target = ++s;
2962306a36Sopenharmony_ci	ops->target.raw = strdup(target);
3062306a36Sopenharmony_ci	if (ops->target.raw == NULL)
3162306a36Sopenharmony_ci		goto out_free_source;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	ops->target.addr = strtoull(target, &endptr, 16);
3462306a36Sopenharmony_ci	if (endptr == target)
3562306a36Sopenharmony_ci		goto out_free_target;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	s = strchr(endptr, '<');
3862306a36Sopenharmony_ci	if (s == NULL)
3962306a36Sopenharmony_ci		goto out_free_target;
4062306a36Sopenharmony_ci	endptr = strchr(s + 1, '>');
4162306a36Sopenharmony_ci	if (endptr == NULL)
4262306a36Sopenharmony_ci		goto out_free_target;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	*endptr = '\0';
4562306a36Sopenharmony_ci	*s = ' ';
4662306a36Sopenharmony_ci	ops->target.name = strdup(s);
4762306a36Sopenharmony_ci	*s = '<';
4862306a36Sopenharmony_ci	*endptr = '>';
4962306a36Sopenharmony_ci	if (ops->target.name == NULL)
5062306a36Sopenharmony_ci		goto out_free_target;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	return 0;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ciout_free_target:
5562306a36Sopenharmony_ci	zfree(&ops->target.raw);
5662306a36Sopenharmony_ciout_free_source:
5762306a36Sopenharmony_ci	zfree(&ops->source.raw);
5862306a36Sopenharmony_ci	return -1;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic int mov__scnprintf(struct ins *ins, char *bf, size_t size,
6262306a36Sopenharmony_ci			  struct ins_operands *ops, int max_ins_name);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic struct ins_ops arm64_mov_ops = {
6562306a36Sopenharmony_ci	.parse	   = arm64_mov__parse,
6662306a36Sopenharmony_ci	.scnprintf = mov__scnprintf,
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic struct ins_ops *arm64__associate_instruction_ops(struct arch *arch, const char *name)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct arm64_annotate *arm = arch->priv;
7262306a36Sopenharmony_ci	struct ins_ops *ops;
7362306a36Sopenharmony_ci	regmatch_t match[2];
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (!regexec(&arm->jump_insn, name, 2, match, 0))
7662306a36Sopenharmony_ci		ops = &jump_ops;
7762306a36Sopenharmony_ci	else if (!regexec(&arm->call_insn, name, 2, match, 0))
7862306a36Sopenharmony_ci		ops = &call_ops;
7962306a36Sopenharmony_ci	else if (!strcmp(name, "ret"))
8062306a36Sopenharmony_ci		ops = &ret_ops;
8162306a36Sopenharmony_ci	else
8262306a36Sopenharmony_ci		ops = &arm64_mov_ops;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	arch__associate_ins_ops(arch, name, ops);
8562306a36Sopenharmony_ci	return ops;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int arm64__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct arm64_annotate *arm;
9162306a36Sopenharmony_ci	int err;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (arch->initialized)
9462306a36Sopenharmony_ci		return 0;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	arm = zalloc(sizeof(*arm));
9762306a36Sopenharmony_ci	if (!arm)
9862306a36Sopenharmony_ci		return ENOMEM;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/* bl, blr */
10162306a36Sopenharmony_ci	err = regcomp(&arm->call_insn, "^blr?$", REG_EXTENDED);
10262306a36Sopenharmony_ci	if (err)
10362306a36Sopenharmony_ci		goto out_free_arm;
10462306a36Sopenharmony_ci	/* b, b.cond, br, cbz/cbnz, tbz/tbnz */
10562306a36Sopenharmony_ci	err = regcomp(&arm->jump_insn, "^[ct]?br?\\.?(cc|cs|eq|ge|gt|hi|hs|le|lo|ls|lt|mi|ne|pl|vc|vs)?n?z?$",
10662306a36Sopenharmony_ci		      REG_EXTENDED);
10762306a36Sopenharmony_ci	if (err)
10862306a36Sopenharmony_ci		goto out_free_call;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	arch->initialized = true;
11162306a36Sopenharmony_ci	arch->priv	  = arm;
11262306a36Sopenharmony_ci	arch->associate_instruction_ops   = arm64__associate_instruction_ops;
11362306a36Sopenharmony_ci	arch->objdump.comment_char	  = '/';
11462306a36Sopenharmony_ci	arch->objdump.skip_functions_char = '+';
11562306a36Sopenharmony_ci	return 0;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ciout_free_call:
11862306a36Sopenharmony_ci	regfree(&arm->call_insn);
11962306a36Sopenharmony_ciout_free_arm:
12062306a36Sopenharmony_ci	free(arm);
12162306a36Sopenharmony_ci	return SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_REGEXP;
12262306a36Sopenharmony_ci}
123