18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/compiler.h>
38c2ecf20Sopenharmony_ci#include <sys/types.h>
48c2ecf20Sopenharmony_ci#include <regex.h>
58c2ecf20Sopenharmony_ci#include <stdlib.h>
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_cistruct arm64_annotate {
88c2ecf20Sopenharmony_ci	regex_t call_insn,
98c2ecf20Sopenharmony_ci		jump_insn;
108c2ecf20Sopenharmony_ci};
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistatic int arm64_mov__parse(struct arch *arch __maybe_unused,
138c2ecf20Sopenharmony_ci			    struct ins_operands *ops,
148c2ecf20Sopenharmony_ci			    struct map_symbol *ms __maybe_unused)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	char *s = strchr(ops->raw, ','), *target, *endptr;
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci	if (s == NULL)
198c2ecf20Sopenharmony_ci		return -1;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	*s = '\0';
228c2ecf20Sopenharmony_ci	ops->source.raw = strdup(ops->raw);
238c2ecf20Sopenharmony_ci	*s = ',';
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	if (ops->source.raw == NULL)
268c2ecf20Sopenharmony_ci		return -1;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	target = ++s;
298c2ecf20Sopenharmony_ci	ops->target.raw = strdup(target);
308c2ecf20Sopenharmony_ci	if (ops->target.raw == NULL)
318c2ecf20Sopenharmony_ci		goto out_free_source;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	ops->target.addr = strtoull(target, &endptr, 16);
348c2ecf20Sopenharmony_ci	if (endptr == target)
358c2ecf20Sopenharmony_ci		goto out_free_target;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	s = strchr(endptr, '<');
388c2ecf20Sopenharmony_ci	if (s == NULL)
398c2ecf20Sopenharmony_ci		goto out_free_target;
408c2ecf20Sopenharmony_ci	endptr = strchr(s + 1, '>');
418c2ecf20Sopenharmony_ci	if (endptr == NULL)
428c2ecf20Sopenharmony_ci		goto out_free_target;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	*endptr = '\0';
458c2ecf20Sopenharmony_ci	*s = ' ';
468c2ecf20Sopenharmony_ci	ops->target.name = strdup(s);
478c2ecf20Sopenharmony_ci	*s = '<';
488c2ecf20Sopenharmony_ci	*endptr = '>';
498c2ecf20Sopenharmony_ci	if (ops->target.name == NULL)
508c2ecf20Sopenharmony_ci		goto out_free_target;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	return 0;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ciout_free_target:
558c2ecf20Sopenharmony_ci	zfree(&ops->target.raw);
568c2ecf20Sopenharmony_ciout_free_source:
578c2ecf20Sopenharmony_ci	zfree(&ops->source.raw);
588c2ecf20Sopenharmony_ci	return -1;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic int mov__scnprintf(struct ins *ins, char *bf, size_t size,
628c2ecf20Sopenharmony_ci			  struct ins_operands *ops, int max_ins_name);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic struct ins_ops arm64_mov_ops = {
658c2ecf20Sopenharmony_ci	.parse	   = arm64_mov__parse,
668c2ecf20Sopenharmony_ci	.scnprintf = mov__scnprintf,
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic struct ins_ops *arm64__associate_instruction_ops(struct arch *arch, const char *name)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct arm64_annotate *arm = arch->priv;
728c2ecf20Sopenharmony_ci	struct ins_ops *ops;
738c2ecf20Sopenharmony_ci	regmatch_t match[2];
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (!regexec(&arm->jump_insn, name, 2, match, 0))
768c2ecf20Sopenharmony_ci		ops = &jump_ops;
778c2ecf20Sopenharmony_ci	else if (!regexec(&arm->call_insn, name, 2, match, 0))
788c2ecf20Sopenharmony_ci		ops = &call_ops;
798c2ecf20Sopenharmony_ci	else if (!strcmp(name, "ret"))
808c2ecf20Sopenharmony_ci		ops = &ret_ops;
818c2ecf20Sopenharmony_ci	else
828c2ecf20Sopenharmony_ci		ops = &arm64_mov_ops;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	arch__associate_ins_ops(arch, name, ops);
858c2ecf20Sopenharmony_ci	return ops;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic int arm64__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct arm64_annotate *arm;
918c2ecf20Sopenharmony_ci	int err;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if (arch->initialized)
948c2ecf20Sopenharmony_ci		return 0;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	arm = zalloc(sizeof(*arm));
978c2ecf20Sopenharmony_ci	if (!arm)
988c2ecf20Sopenharmony_ci		return ENOMEM;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/* bl, blr */
1018c2ecf20Sopenharmony_ci	err = regcomp(&arm->call_insn, "^blr?$", REG_EXTENDED);
1028c2ecf20Sopenharmony_ci	if (err)
1038c2ecf20Sopenharmony_ci		goto out_free_arm;
1048c2ecf20Sopenharmony_ci	/* b, b.cond, br, cbz/cbnz, tbz/tbnz */
1058c2ecf20Sopenharmony_ci	err = regcomp(&arm->jump_insn, "^[ct]?br?\\.?(cc|cs|eq|ge|gt|hi|le|ls|lt|mi|ne|pl)?n?z?$",
1068c2ecf20Sopenharmony_ci		      REG_EXTENDED);
1078c2ecf20Sopenharmony_ci	if (err)
1088c2ecf20Sopenharmony_ci		goto out_free_call;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	arch->initialized = true;
1118c2ecf20Sopenharmony_ci	arch->priv	  = arm;
1128c2ecf20Sopenharmony_ci	arch->associate_instruction_ops   = arm64__associate_instruction_ops;
1138c2ecf20Sopenharmony_ci	arch->objdump.comment_char	  = '/';
1148c2ecf20Sopenharmony_ci	arch->objdump.skip_functions_char = '+';
1158c2ecf20Sopenharmony_ci	return 0;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ciout_free_call:
1188c2ecf20Sopenharmony_ci	regfree(&arm->call_insn);
1198c2ecf20Sopenharmony_ciout_free_arm:
1208c2ecf20Sopenharmony_ci	free(arm);
1218c2ecf20Sopenharmony_ci	return SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_REGEXP;
1228c2ecf20Sopenharmony_ci}
123