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 ssize_t get_uprobe_offset(const void *addr) 142 { 143 size_t start, end, base; 144 char buf[256]; 145 bool found = false; 146 FILE *f; 147 148 f = fopen("/proc/self/maps", "r"); 149 if (!f) 150 return -errno; 151 152 while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) == 4) { 153 if (buf[2] == 'x' && (uintptr_t)addr >= start && (uintptr_t)addr < end) { 154 found = true; 155 break; 156 } 157 } 158 159 fclose(f); 160 161 if (!found) 162 return -ESRCH; 163 164 #if defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2 165 166 #define OP_RT_RA_MASK 0xffff0000UL 167 #define LIS_R2 0x3c400000UL 168 #define ADDIS_R2_R12 0x3c4c0000UL 169 #define ADDI_R2_R2 0x38420000UL 170 171 /* 172 * A PPC64 ABIv2 function may have a local and a global entry 173 * point. We need to use the local entry point when patching 174 * functions, so identify and step over the global entry point 175 * sequence. 176 * 177 * The global entry point sequence is always of the form: 178 * 179 * addis r2,r12,XXXX 180 * addi r2,r2,XXXX 181 * 182 * A linker optimisation may convert the addis to lis: 183 * 184 * lis r2,XXXX 185 * addi r2,r2,XXXX 186 */ 187 { 188 const u32 *insn = (const u32 *)(uintptr_t)addr; 189 190 if ((((*insn & OP_RT_RA_MASK) == ADDIS_R2_R12) || 191 ((*insn & OP_RT_RA_MASK) == LIS_R2)) && 192 ((*(insn + 1) & OP_RT_RA_MASK) == ADDI_R2_R2)) 193 return (uintptr_t)(insn + 2) - start + base; 194 } 195 #endif 196 return (uintptr_t)addr - start + base; 197 } 198 199 ssize_t get_rel_offset(uintptr_t addr) 200 { 201 size_t start, end, offset; 202 char buf[256]; 203 FILE *f; 204 205 f = fopen("/proc/self/maps", "r"); 206 if (!f) 207 return -errno; 208 209 while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &offset) == 4) { 210 if (addr >= start && addr < end) { 211 fclose(f); 212 return (size_t)addr - start + offset; 213 } 214 } 215 216 fclose(f); 217 return -EINVAL; 218 } 219