// SPDX-License-Identifier: GPL-2.0 /* * Perf annotate functions. * * Copyright (C) 2020-2023 Loongson Technology Corporation Limited */ #include #include #include #include #include "../disasm.h" #include "../map.h" #include "../maps.h" #include "../symbol.h" #include "../thread.h" static int loongarch_call__parse(const struct arch *arch, struct ins_operands *ops, struct map_symbol *ms, struct disasm_line *dl __maybe_unused) { char *c, *endptr, *tok, *name; struct map *map = ms->map; struct addr_map_symbol target; c = strchr(ops->raw, '#'); if (c++ == NULL) return -1; ops->target.addr = strtoull(c, &endptr, 16); name = strchr(endptr, '<'); 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; 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; } static const struct ins_ops loongarch_call_ops = { .parse = loongarch_call__parse, .scnprintf = call__scnprintf, .is_call = true, }; static int loongarch_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, '<'); if (ops->jump.raw_func_start && c > ops->jump.raw_func_start) c = NULL; 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; 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; } static const struct ins_ops loongarch_jump_ops = { .parse = loongarch_jump__parse, .scnprintf = jump__scnprintf, .is_jump = true, }; static const struct ins_ops *loongarch__associate_ins_ops(struct arch *arch, const char *name) { const struct ins_ops *ops = NULL; if (!strcmp(name, "bl")) ops = &loongarch_call_ops; else if (!strcmp(name, "jirl")) ops = &ret_ops; else if (!strcmp(name, "b") || !strncmp(name, "beq", 3) || !strncmp(name, "bne", 3) || !strncmp(name, "blt", 3) || !strncmp(name, "bge", 3) || !strncmp(name, "bltu", 4) || !strncmp(name, "bgeu", 4)) ops = &loongarch_jump_ops; else return NULL; arch__associate_ins_ops(arch, name, ops); return ops; } const struct arch *arch__new_loongarch(const struct e_machine_and_e_flags *id, const char *cpuid __maybe_unused) { struct arch *arch = zalloc(sizeof(*arch)); if (!arch) return NULL; arch->name = "loongarch"; arch->id = *id; arch->associate_instruction_ops = loongarch__associate_ins_ops; arch->objdump.comment_char = '#'; return arch; }