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