1 // SPDX-License-Identifier: GPL-2.0 2 #include <ctype.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <assert.h> 7 #include <errno.h> 8 #include <fcntl.h> 9 #include <poll.h> 10 #include <unistd.h> 11 #include <linux/perf_event.h> 12 #include <sys/mman.h> 13 #include "trace_helpers.h" 14 15 #define DEBUGFS "/sys/kernel/debug/tracing/" 16 17 #define MAX_SYMS 300000 18 static struct ksym syms[MAX_SYMS]; 19 static int sym_cnt; 20 21 static int ksym_cmp(const void *p1, const void *p2) 22 { 23 return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr; 24 } 25 26 int load_kallsyms(void) 27 { 28 FILE *f = fopen("/proc/kallsyms", "r"); 29 char func[256], buf[256]; 30 char symbol; 31 void *addr; 32 int i = 0; 33 34 if (!f) 35 return -ENOENT; 36 37 while (fgets(buf, sizeof(buf), f)) { 38 if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3) 39 break; 40 if (!addr) 41 continue; 42 syms[i].addr = (long) addr; 43 syms[i].name = strdup(func); 44 i++; 45 } 46 fclose(f); 47 sym_cnt = i; 48 qsort(syms, sym_cnt, sizeof(struct ksym), ksym_cmp); 49 return 0; 50 } 51 52 struct ksym *ksym_search(long key) 53 { 54 int start = 0, end = sym_cnt; 55 int result; 56 57 /* kallsyms not loaded. return NULL */ 58 if (sym_cnt <= 0) 59 return NULL; 60 61 while (start < end) { 62 size_t mid = start + (end - start) / 2; 63 64 result = key - syms[mid].addr; 65 if (result < 0) 66 end = mid; 67 else if (result > 0) 68 start = mid + 1; 69 else 70 return &syms[mid]; 71 } 72 73 if (start >= 1 && syms[start - 1].addr < key && 74 key < syms[start].addr) 75 /* valid ksym */ 76 return &syms[start - 1]; 77 78 /* out of range. return _stext */ 79 return &syms[0]; 80 } 81 82 long ksym_get_addr(const char *name) 83 { 84 int i; 85 86 for (i = 0; i < sym_cnt; i++) { 87 if (strcmp(syms[i].name, name) == 0) 88 return syms[i].addr; 89 } 90 91 return 0; 92 } 93 94 /* open kallsyms and read symbol addresses on the fly. Without caching all symbols, 95 * this is faster than load + find. 96 */ 97 int kallsyms_find(const char *sym, unsigned long long *addr) 98 { 99 char type, name[500]; 100 unsigned long long value; 101 int err = 0; 102 FILE *f; 103 104 f = fopen("/proc/kallsyms", "r"); 105 if (!f) 106 return -EINVAL; 107 108 while (fscanf(f, "%llx %c %499s%*[^\n]\n", &value, &type, name) > 0) { 109 if (strcmp(name, sym) == 0) { 110 *addr = value; 111 goto out; 112 } 113 } 114 err = -ENOENT; 115 116 out: 117 fclose(f); 118 return err; 119 } 120 121 void read_trace_pipe(void) 122 { 123 int trace_fd; 124 125 trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0); 126 if (trace_fd < 0) 127 return; 128 129 while (1) { 130 static char buf[4096]; 131 ssize_t sz; 132 133 sz = read(trace_fd, buf, sizeof(buf) - 1); 134 if (sz > 0) { 135 buf[sz] = 0; 136 puts(buf); 137 } 138 } 139 } 140 141 #if defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2 142 143 #define OP_RT_RA_MASK 0xffff0000UL 144 #define LIS_R2 0x3c400000UL 145 #define ADDIS_R2_R12 0x3c4c0000UL 146 #define ADDI_R2_R2 0x38420000UL 147 148 ssize_t get_uprobe_offset(const void *addr, ssize_t base) 149 { 150 u32 *insn = (u32 *)(uintptr_t)addr; 151 152 /* 153 * A PPC64 ABIv2 function may have a local and a global entry 154 * point. We need to use the local entry point when patching 155 * functions, so identify and step over the global entry point 156 * sequence. 157 * 158 * The global entry point sequence is always of the form: 159 * 160 * addis r2,r12,XXXX 161 * addi r2,r2,XXXX 162 * 163 * A linker optimisation may convert the addis to lis: 164 * 165 * lis r2,XXXX 166 * addi r2,r2,XXXX 167 */ 168 if ((((*insn & OP_RT_RA_MASK) == ADDIS_R2_R12) || 169 ((*insn & OP_RT_RA_MASK) == LIS_R2)) && 170 ((*(insn + 1) & OP_RT_RA_MASK) == ADDI_R2_R2)) 171 return (ssize_t)(insn + 2) - base; 172 else 173 return (uintptr_t)addr - base; 174 } 175 176 #else 177 178 ssize_t get_uprobe_offset(const void *addr, ssize_t base) 179 { 180 return (uintptr_t)addr - base; 181 } 182 183 #endif 184 185 ssize_t get_base_addr(void) 186 { 187 size_t start, offset; 188 char buf[256]; 189 FILE *f; 190 191 f = fopen("/proc/self/maps", "r"); 192 if (!f) 193 return -errno; 194 195 while (fscanf(f, "%zx-%*x %s %zx %*[^\n]\n", 196 &start, buf, &offset) == 3) { 197 if (strcmp(buf, "r-xp") == 0) { 198 fclose(f); 199 return start - offset; 200 } 201 } 202 203 fclose(f); 204 return -EINVAL; 205 } 206 207 ssize_t get_rel_offset(uintptr_t addr) 208 { 209 size_t start, end, offset; 210 char buf[256]; 211 FILE *f; 212 213 f = fopen("/proc/self/maps", "r"); 214 if (!f) 215 return -errno; 216 217 while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &offset) == 4) { 218 if (addr >= start && addr < end) { 219 fclose(f); 220 return (size_t)addr - start + offset; 221 } 222 } 223 224 fclose(f); 225 return -EINVAL; 226 } 227