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 /* find the address of the next symbol of the same type, this can be used 122 * to determine the end of a function. 123 */ 124 int kallsyms_find_next(const char *sym, unsigned long long *addr) 125 { 126 char type, found_type, name[500]; 127 unsigned long long value; 128 bool found = false; 129 int err = 0; 130 FILE *f; 131 132 f = fopen("/proc/kallsyms", "r"); 133 if (!f) 134 return -EINVAL; 135 136 while (fscanf(f, "%llx %c %499s%*[^\n]\n", &value, &type, name) > 0) { 137 /* Different types of symbols in kernel modules are mixed 138 * in /proc/kallsyms. Only return the next matching type. 139 * Use tolower() for type so that 'T' matches 't'. 140 */ 141 if (found && found_type == tolower(type)) { 142 *addr = value; 143 goto out; 144 } 145 if (strcmp(name, sym) == 0) { 146 found = true; 147 found_type = tolower(type); 148 } 149 } 150 err = -ENOENT; 151 152 out: 153 fclose(f); 154 return err; 155 } 156 157 void read_trace_pipe(void) 158 { 159 int trace_fd; 160 161 trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0); 162 if (trace_fd < 0) 163 return; 164 165 while (1) { 166 static char buf[4096]; 167 ssize_t sz; 168 169 sz = read(trace_fd, buf, sizeof(buf) - 1); 170 if (sz > 0) { 171 buf[sz] = 0; 172 puts(buf); 173 } 174 } 175 } 176 177 #if defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2 178 179 #define OP_RT_RA_MASK 0xffff0000UL 180 #define LIS_R2 0x3c400000UL 181 #define ADDIS_R2_R12 0x3c4c0000UL 182 #define ADDI_R2_R2 0x38420000UL 183 184 ssize_t get_uprobe_offset(const void *addr, ssize_t base) 185 { 186 u32 *insn = (u32 *)(uintptr_t)addr; 187 188 /* 189 * A PPC64 ABIv2 function may have a local and a global entry 190 * point. We need to use the local entry point when patching 191 * functions, so identify and step over the global entry point 192 * sequence. 193 * 194 * The global entry point sequence is always of the form: 195 * 196 * addis r2,r12,XXXX 197 * addi r2,r2,XXXX 198 * 199 * A linker optimisation may convert the addis to lis: 200 * 201 * lis r2,XXXX 202 * addi r2,r2,XXXX 203 */ 204 if ((((*insn & OP_RT_RA_MASK) == ADDIS_R2_R12) || 205 ((*insn & OP_RT_RA_MASK) == LIS_R2)) && 206 ((*(insn + 1) & OP_RT_RA_MASK) == ADDI_R2_R2)) 207 return (ssize_t)(insn + 2) - base; 208 else 209 return (uintptr_t)addr - base; 210 } 211 212 #else 213 214 ssize_t get_uprobe_offset(const void *addr, ssize_t base) 215 { 216 return (uintptr_t)addr - base; 217 } 218 219 #endif 220 221 ssize_t get_base_addr(void) 222 { 223 size_t start, offset; 224 char buf[256]; 225 FILE *f; 226 227 f = fopen("/proc/self/maps", "r"); 228 if (!f) 229 return -errno; 230 231 while (fscanf(f, "%zx-%*x %s %zx %*[^\n]\n", 232 &start, buf, &offset) == 3) { 233 if (strcmp(buf, "r-xp") == 0) { 234 fclose(f); 235 return start - offset; 236 } 237 } 238 239 fclose(f); 240 return -EINVAL; 241 } 242 243 ssize_t get_rel_offset(uintptr_t addr) 244 { 245 size_t start, end, offset; 246 char buf[256]; 247 FILE *f; 248 249 f = fopen("/proc/self/maps", "r"); 250 if (!f) 251 return -errno; 252 253 while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &offset) == 4) { 254 if (addr >= start && addr < end) { 255 fclose(f); 256 return (size_t)addr - start + offset; 257 } 258 } 259 260 fclose(f); 261 return -EINVAL; 262 } 263