1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Perf annotate functions. 4 * 5 * Copyright (C) 2020-2023 Loongson Technology Corporation Limited 6 */ 7 #include <stdlib.h> 8 #include <string.h> 9 #include <linux/compiler.h> 10 #include <linux/zalloc.h> 11 #include "../disasm.h" 12 #include "../map.h" 13 #include "../maps.h" 14 #include "../symbol.h" 15 #include "../thread.h" 16 17 static int loongarch_call__parse(const struct arch *arch, struct ins_operands *ops, 18 struct map_symbol *ms, 19 struct disasm_line *dl __maybe_unused) 20 { 21 char *c, *endptr, *tok, *name; 22 struct map *map = ms->map; 23 struct addr_map_symbol target; 24 25 c = strchr(ops->raw, '#'); 26 if (c++ == NULL) 27 return -1; 28 29 ops->target.addr = strtoull(c, &endptr, 16); 30 31 name = strchr(endptr, '<'); 32 name++; 33 34 if (arch->objdump.skip_functions_char && 35 strchr(name, arch->objdump.skip_functions_char)) 36 return -1; 37 38 tok = strchr(name, '>'); 39 if (tok == NULL) 40 return -1; 41 42 *tok = '\0'; 43 ops->target.name = strdup(name); 44 *tok = '>'; 45 46 if (ops->target.name == NULL) 47 return -1; 48 49 target = (struct addr_map_symbol) { 50 .ms = { .map = map__get(map), }, 51 .addr = map__objdump_2mem(map, ops->target.addr), 52 }; 53 54 if (maps__find_ams(thread__maps(ms->thread), &target) == 0 && 55 map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr) 56 ops->target.sym = target.ms.sym; 57 58 addr_map_symbol__exit(&target); 59 return 0; 60 } 61 62 static const struct ins_ops loongarch_call_ops = { 63 .parse = loongarch_call__parse, 64 .scnprintf = call__scnprintf, 65 .is_call = true, 66 }; 67 68 static int loongarch_jump__parse(const struct arch *arch, struct ins_operands *ops, 69 struct map_symbol *ms, 70 struct disasm_line *dl __maybe_unused) 71 72 { 73 struct map *map = ms->map; 74 struct symbol *sym = ms->sym; 75 struct addr_map_symbol target = { 76 .ms = { .map = map__get(map), }, 77 }; 78 const char *c = strchr(ops->raw, '#'); 79 u64 start, end; 80 81 ops->jump.raw_comment = strchr(ops->raw, arch->objdump.comment_char); 82 ops->jump.raw_func_start = strchr(ops->raw, '<'); 83 84 if (ops->jump.raw_func_start && c > ops->jump.raw_func_start) 85 c = NULL; 86 87 if (c++ != NULL) 88 ops->target.addr = strtoull(c, NULL, 16); 89 else 90 ops->target.addr = strtoull(ops->raw, NULL, 16); 91 92 target.addr = map__objdump_2mem(map, ops->target.addr); 93 start = map__unmap_ip(map, sym->start); 94 end = map__unmap_ip(map, sym->end); 95 96 ops->target.outside = target.addr < start || target.addr > end; 97 98 if (maps__find_ams(thread__maps(ms->thread), &target) == 0 && 99 map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr) 100 ops->target.sym = target.ms.sym; 101 102 if (!ops->target.outside) { 103 ops->target.offset = target.addr - start; 104 ops->target.offset_avail = true; 105 } else { 106 ops->target.offset_avail = false; 107 } 108 addr_map_symbol__exit(&target); 109 return 0; 110 } 111 112 static const struct ins_ops loongarch_jump_ops = { 113 .parse = loongarch_jump__parse, 114 .scnprintf = jump__scnprintf, 115 .is_jump = true, 116 }; 117 118 static 119 const struct ins_ops *loongarch__associate_ins_ops(struct arch *arch, const char *name) 120 { 121 const struct ins_ops *ops = NULL; 122 123 if (!strcmp(name, "bl")) 124 ops = &loongarch_call_ops; 125 else if (!strcmp(name, "jirl")) 126 ops = &ret_ops; 127 else if (!strcmp(name, "b") || 128 !strncmp(name, "beq", 3) || 129 !strncmp(name, "bne", 3) || 130 !strncmp(name, "blt", 3) || 131 !strncmp(name, "bge", 3) || 132 !strncmp(name, "bltu", 4) || 133 !strncmp(name, "bgeu", 4)) 134 ops = &loongarch_jump_ops; 135 else 136 return NULL; 137 138 arch__associate_ins_ops(arch, name, ops); 139 140 return ops; 141 } 142 143 const struct arch *arch__new_loongarch(const struct e_machine_and_e_flags *id, 144 const char *cpuid __maybe_unused) 145 { 146 struct arch *arch = zalloc(sizeof(*arch)); 147 148 if (!arch) 149 return NULL; 150 151 arch->name = "loongarch"; 152 arch->id = *id; 153 arch->associate_instruction_ops = loongarch__associate_ins_ops; 154 arch->objdump.comment_char = '#'; 155 return arch; 156 } 157