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 #include <linux/limits.h>
15 #include <libelf.h>
16 #include <gelf.h>
17
18 #define TRACEFS_PIPE "/sys/kernel/tracing/trace_pipe"
19 #define DEBUGFS_PIPE "/sys/kernel/debug/tracing/trace_pipe"
20
21 #define MAX_SYMS 400000
22 static struct ksym syms[MAX_SYMS];
23 static int sym_cnt;
24
ksym_cmp(const void * p1,const void * p2)25 static int ksym_cmp(const void *p1, const void *p2)
26 {
27 return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr;
28 }
29
load_kallsyms_refresh(void)30 int load_kallsyms_refresh(void)
31 {
32 FILE *f;
33 char func[256], buf[256];
34 char symbol;
35 void *addr;
36 int i = 0;
37
38 sym_cnt = 0;
39
40 f = fopen("/proc/kallsyms", "r");
41 if (!f)
42 return -ENOENT;
43
44 while (fgets(buf, sizeof(buf), f)) {
45 if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3)
46 break;
47 if (!addr)
48 continue;
49 if (i >= MAX_SYMS)
50 return -EFBIG;
51
52 syms[i].addr = (long) addr;
53 syms[i].name = strdup(func);
54 i++;
55 }
56 fclose(f);
57 sym_cnt = i;
58 qsort(syms, sym_cnt, sizeof(struct ksym), ksym_cmp);
59 return 0;
60 }
61
load_kallsyms(void)62 int load_kallsyms(void)
63 {
64 /*
65 * This is called/used from multiplace places,
66 * load symbols just once.
67 */
68 if (sym_cnt)
69 return 0;
70 return load_kallsyms_refresh();
71 }
72
ksym_search(long key)73 struct ksym *ksym_search(long key)
74 {
75 int start = 0, end = sym_cnt;
76 int result;
77
78 /* kallsyms not loaded. return NULL */
79 if (sym_cnt <= 0)
80 return NULL;
81
82 while (start < end) {
83 size_t mid = start + (end - start) / 2;
84
85 result = key - syms[mid].addr;
86 if (result < 0)
87 end = mid;
88 else if (result > 0)
89 start = mid + 1;
90 else
91 return &syms[mid];
92 }
93
94 if (start >= 1 && syms[start - 1].addr < key &&
95 key < syms[start].addr)
96 /* valid ksym */
97 return &syms[start - 1];
98
99 /* out of range. return _stext */
100 return &syms[0];
101 }
102
ksym_get_addr(const char * name)103 long ksym_get_addr(const char *name)
104 {
105 int i;
106
107 for (i = 0; i < sym_cnt; i++) {
108 if (strcmp(syms[i].name, name) == 0)
109 return syms[i].addr;
110 }
111
112 return 0;
113 }
114
115 /* open kallsyms and read symbol addresses on the fly. Without caching all symbols,
116 * this is faster than load + find.
117 */
kallsyms_find(const char * sym,unsigned long long * addr)118 int kallsyms_find(const char *sym, unsigned long long *addr)
119 {
120 char type, name[500];
121 unsigned long long value;
122 int err = 0;
123 FILE *f;
124
125 f = fopen("/proc/kallsyms", "r");
126 if (!f)
127 return -EINVAL;
128
129 while (fscanf(f, "%llx %c %499s%*[^\n]\n", &value, &type, name) > 0) {
130 if (strcmp(name, sym) == 0) {
131 *addr = value;
132 goto out;
133 }
134 }
135 err = -ENOENT;
136
137 out:
138 fclose(f);
139 return err;
140 }
141
read_trace_pipe(void)142 void read_trace_pipe(void)
143 {
144 int trace_fd;
145
146 if (access(TRACEFS_PIPE, F_OK) == 0)
147 trace_fd = open(TRACEFS_PIPE, O_RDONLY, 0);
148 else
149 trace_fd = open(DEBUGFS_PIPE, O_RDONLY, 0);
150 if (trace_fd < 0)
151 return;
152
153 while (1) {
154 static char buf[4096];
155 ssize_t sz;
156
157 sz = read(trace_fd, buf, sizeof(buf) - 1);
158 if (sz > 0) {
159 buf[sz] = 0;
160 puts(buf);
161 }
162 }
163 }
164
get_uprobe_offset(const void * addr)165 ssize_t get_uprobe_offset(const void *addr)
166 {
167 size_t start, end, base;
168 char buf[256];
169 bool found = false;
170 FILE *f;
171
172 f = fopen("/proc/self/maps", "r");
173 if (!f)
174 return -errno;
175
176 while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) == 4) {
177 if (buf[2] == 'x' && (uintptr_t)addr >= start && (uintptr_t)addr < end) {
178 found = true;
179 break;
180 }
181 }
182
183 fclose(f);
184
185 if (!found)
186 return -ESRCH;
187
188 #if defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2
189
190 #define OP_RT_RA_MASK 0xffff0000UL
191 #define LIS_R2 0x3c400000UL
192 #define ADDIS_R2_R12 0x3c4c0000UL
193 #define ADDI_R2_R2 0x38420000UL
194
195 /*
196 * A PPC64 ABIv2 function may have a local and a global entry
197 * point. We need to use the local entry point when patching
198 * functions, so identify and step over the global entry point
199 * sequence.
200 *
201 * The global entry point sequence is always of the form:
202 *
203 * addis r2,r12,XXXX
204 * addi r2,r2,XXXX
205 *
206 * A linker optimisation may convert the addis to lis:
207 *
208 * lis r2,XXXX
209 * addi r2,r2,XXXX
210 */
211 {
212 const u32 *insn = (const u32 *)(uintptr_t)addr;
213
214 if ((((*insn & OP_RT_RA_MASK) == ADDIS_R2_R12) ||
215 ((*insn & OP_RT_RA_MASK) == LIS_R2)) &&
216 ((*(insn + 1) & OP_RT_RA_MASK) == ADDI_R2_R2))
217 return (uintptr_t)(insn + 2) - start + base;
218 }
219 #endif
220 return (uintptr_t)addr - start + base;
221 }
222
get_rel_offset(uintptr_t addr)223 ssize_t get_rel_offset(uintptr_t addr)
224 {
225 size_t start, end, offset;
226 char buf[256];
227 FILE *f;
228
229 f = fopen("/proc/self/maps", "r");
230 if (!f)
231 return -errno;
232
233 while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &offset) == 4) {
234 if (addr >= start && addr < end) {
235 fclose(f);
236 return (size_t)addr - start + offset;
237 }
238 }
239
240 fclose(f);
241 return -EINVAL;
242 }
243
244 static int
parse_build_id_buf(const void * note_start,Elf32_Word note_size,char * build_id)245 parse_build_id_buf(const void *note_start, Elf32_Word note_size, char *build_id)
246 {
247 Elf32_Word note_offs = 0;
248
249 while (note_offs + sizeof(Elf32_Nhdr) < note_size) {
250 Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs);
251
252 if (nhdr->n_type == 3 && nhdr->n_namesz == sizeof("GNU") &&
253 !strcmp((char *)(nhdr + 1), "GNU") && nhdr->n_descsz > 0 &&
254 nhdr->n_descsz <= BPF_BUILD_ID_SIZE) {
255 memcpy(build_id, note_start + note_offs +
256 ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr), nhdr->n_descsz);
257 memset(build_id + nhdr->n_descsz, 0, BPF_BUILD_ID_SIZE - nhdr->n_descsz);
258 return (int) nhdr->n_descsz;
259 }
260
261 note_offs = note_offs + sizeof(Elf32_Nhdr) +
262 ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4);
263 }
264
265 return -ENOENT;
266 }
267
268 /* Reads binary from *path* file and returns it in the *build_id* buffer
269 * with *size* which is expected to be at least BPF_BUILD_ID_SIZE bytes.
270 * Returns size of build id on success. On error the error value is
271 * returned.
272 */
read_build_id(const char * path,char * build_id,size_t size)273 int read_build_id(const char *path, char *build_id, size_t size)
274 {
275 int fd, err = -EINVAL;
276 Elf *elf = NULL;
277 GElf_Ehdr ehdr;
278 size_t max, i;
279
280 if (size < BPF_BUILD_ID_SIZE)
281 return -EINVAL;
282
283 fd = open(path, O_RDONLY | O_CLOEXEC);
284 if (fd < 0)
285 return -errno;
286
287 (void)elf_version(EV_CURRENT);
288
289 elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
290 if (!elf)
291 goto out;
292 if (elf_kind(elf) != ELF_K_ELF)
293 goto out;
294 if (!gelf_getehdr(elf, &ehdr))
295 goto out;
296
297 for (i = 0; i < ehdr.e_phnum; i++) {
298 GElf_Phdr mem, *phdr;
299 char *data;
300
301 phdr = gelf_getphdr(elf, i, &mem);
302 if (!phdr)
303 goto out;
304 if (phdr->p_type != PT_NOTE)
305 continue;
306 data = elf_rawfile(elf, &max);
307 if (!data)
308 goto out;
309 if (phdr->p_offset + phdr->p_memsz > max)
310 goto out;
311 err = parse_build_id_buf(data + phdr->p_offset, phdr->p_memsz, build_id);
312 if (err > 0)
313 break;
314 }
315
316 out:
317 if (elf)
318 elf_end(elf);
319 close(fd);
320 return err;
321 }
322