Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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 <pthread.h>
11#include <unistd.h>
12#include <linux/perf_event.h>
13#include <sys/mman.h>
14#include "trace_helpers.h"
15#include <linux/limits.h>
16#include <libelf.h>
17#include <gelf.h>
18#include "bpf/libbpf_internal.h"
19
20#define TRACEFS_PIPE "/sys/kernel/tracing/trace_pipe"
21#define DEBUGFS_PIPE "/sys/kernel/debug/tracing/trace_pipe"
22
23struct ksyms {
24 struct ksym *syms;
25 size_t sym_cap;
26 size_t sym_cnt;
27};
28
29static struct ksyms *ksyms;
30static pthread_mutex_t ksyms_mutex = PTHREAD_MUTEX_INITIALIZER;
31
32static int ksyms__add_symbol(struct ksyms *ksyms, const char *name,
33 unsigned long addr)
34{
35 void *tmp;
36
37 tmp = strdup(name);
38 if (!tmp)
39 return -ENOMEM;
40 ksyms->syms[ksyms->sym_cnt].addr = addr;
41 ksyms->syms[ksyms->sym_cnt].name = tmp;
42 ksyms->sym_cnt++;
43 return 0;
44}
45
46void free_kallsyms_local(struct ksyms *ksyms)
47{
48 unsigned int i;
49
50 if (!ksyms)
51 return;
52
53 if (!ksyms->syms) {
54 free(ksyms);
55 return;
56 }
57
58 for (i = 0; i < ksyms->sym_cnt; i++)
59 free(ksyms->syms[i].name);
60 free(ksyms->syms);
61 free(ksyms);
62}
63
64static struct ksyms *load_kallsyms_local_common(ksym_cmp_t cmp_cb)
65{
66 FILE *f;
67 char func[256], buf[256];
68 char symbol;
69 void *addr;
70 int ret;
71 struct ksyms *ksyms;
72
73 f = fopen("/proc/kallsyms", "r");
74 if (!f)
75 return NULL;
76
77 ksyms = calloc(1, sizeof(struct ksyms));
78 if (!ksyms) {
79 fclose(f);
80 return NULL;
81 }
82
83 while (fgets(buf, sizeof(buf), f)) {
84 if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3)
85 break;
86 if (!addr)
87 continue;
88
89 ret = libbpf_ensure_mem((void **) &ksyms->syms, &ksyms->sym_cap,
90 sizeof(struct ksym), ksyms->sym_cnt + 1);
91 if (ret)
92 goto error;
93 ret = ksyms__add_symbol(ksyms, func, (unsigned long)addr);
94 if (ret)
95 goto error;
96 }
97 fclose(f);
98 qsort(ksyms->syms, ksyms->sym_cnt, sizeof(struct ksym), cmp_cb);
99 return ksyms;
100
101error:
102 fclose(f);
103 free_kallsyms_local(ksyms);
104 return NULL;
105}
106
107static int ksym_cmp(const void *p1, const void *p2)
108{
109 return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr;
110}
111
112struct ksyms *load_kallsyms_local(void)
113{
114 return load_kallsyms_local_common(ksym_cmp);
115}
116
117struct ksyms *load_kallsyms_custom_local(ksym_cmp_t cmp_cb)
118{
119 return load_kallsyms_local_common(cmp_cb);
120}
121
122int load_kallsyms(void)
123{
124 pthread_mutex_lock(&ksyms_mutex);
125 if (!ksyms)
126 ksyms = load_kallsyms_local();
127 pthread_mutex_unlock(&ksyms_mutex);
128 return ksyms ? 0 : 1;
129}
130
131struct ksym *ksym_search_local(struct ksyms *ksyms, long key)
132{
133 int start = 0, end = ksyms->sym_cnt;
134 int result;
135
136 /* kallsyms not loaded. return NULL */
137 if (ksyms->sym_cnt <= 0)
138 return NULL;
139
140 while (start < end) {
141 size_t mid = start + (end - start) / 2;
142
143 result = key - ksyms->syms[mid].addr;
144 if (result < 0)
145 end = mid;
146 else if (result > 0)
147 start = mid + 1;
148 else
149 return &ksyms->syms[mid];
150 }
151
152 if (start >= 1 && ksyms->syms[start - 1].addr < key &&
153 key < ksyms->syms[start].addr)
154 /* valid ksym */
155 return &ksyms->syms[start - 1];
156
157 /* out of range. return _stext */
158 return &ksyms->syms[0];
159}
160
161struct ksym *search_kallsyms_custom_local(struct ksyms *ksyms, const void *p,
162 ksym_search_cmp_t cmp_cb)
163{
164 int start = 0, mid, end = ksyms->sym_cnt;
165 struct ksym *ks;
166 int result;
167
168 while (start < end) {
169 mid = start + (end - start) / 2;
170 ks = &ksyms->syms[mid];
171 result = cmp_cb(p, ks);
172 if (result < 0)
173 end = mid;
174 else if (result > 0)
175 start = mid + 1;
176 else
177 return ks;
178 }
179
180 return NULL;
181}
182
183struct ksym *ksym_search(long key)
184{
185 if (!ksyms)
186 return NULL;
187 return ksym_search_local(ksyms, key);
188}
189
190long ksym_get_addr_local(struct ksyms *ksyms, const char *name)
191{
192 int i;
193
194 for (i = 0; i < ksyms->sym_cnt; i++) {
195 if (strcmp(ksyms->syms[i].name, name) == 0)
196 return ksyms->syms[i].addr;
197 }
198
199 return 0;
200}
201
202long ksym_get_addr(const char *name)
203{
204 if (!ksyms)
205 return 0;
206 return ksym_get_addr_local(ksyms, name);
207}
208
209/* open kallsyms and read symbol addresses on the fly. Without caching all symbols,
210 * this is faster than load + find.
211 */
212int kallsyms_find(const char *sym, unsigned long long *addr)
213{
214 char type, name[500];
215 unsigned long long value;
216 int err = 0;
217 FILE *f;
218
219 f = fopen("/proc/kallsyms", "r");
220 if (!f)
221 return -EINVAL;
222
223 while (fscanf(f, "%llx %c %499s%*[^\n]\n", &value, &type, name) > 0) {
224 if (strcmp(name, sym) == 0) {
225 *addr = value;
226 goto out;
227 }
228 }
229 err = -ENOENT;
230
231out:
232 fclose(f);
233 return err;
234}
235
236ssize_t get_uprobe_offset(const void *addr)
237{
238 size_t start, end, base;
239 char buf[256];
240 bool found = false;
241 FILE *f;
242
243 f = fopen("/proc/self/maps", "r");
244 if (!f)
245 return -errno;
246
247 while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) == 4) {
248 if (buf[2] == 'x' && (uintptr_t)addr >= start && (uintptr_t)addr < end) {
249 found = true;
250 break;
251 }
252 }
253
254 fclose(f);
255
256 if (!found)
257 return -ESRCH;
258
259#if defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2
260
261#define OP_RT_RA_MASK 0xffff0000UL
262#define LIS_R2 0x3c400000UL
263#define ADDIS_R2_R12 0x3c4c0000UL
264#define ADDI_R2_R2 0x38420000UL
265
266 /*
267 * A PPC64 ABIv2 function may have a local and a global entry
268 * point. We need to use the local entry point when patching
269 * functions, so identify and step over the global entry point
270 * sequence.
271 *
272 * The global entry point sequence is always of the form:
273 *
274 * addis r2,r12,XXXX
275 * addi r2,r2,XXXX
276 *
277 * A linker optimisation may convert the addis to lis:
278 *
279 * lis r2,XXXX
280 * addi r2,r2,XXXX
281 */
282 {
283 const __u32 *insn = (const __u32 *)(uintptr_t)addr;
284
285 if ((((*insn & OP_RT_RA_MASK) == ADDIS_R2_R12) ||
286 ((*insn & OP_RT_RA_MASK) == LIS_R2)) &&
287 ((*(insn + 1) & OP_RT_RA_MASK) == ADDI_R2_R2))
288 return (uintptr_t)(insn + 2) - start + base;
289 }
290#endif
291 return (uintptr_t)addr - start + base;
292}
293
294ssize_t get_rel_offset(uintptr_t addr)
295{
296 size_t start, end, offset;
297 char buf[256];
298 FILE *f;
299
300 f = fopen("/proc/self/maps", "r");
301 if (!f)
302 return -errno;
303
304 while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &offset) == 4) {
305 if (addr >= start && addr < end) {
306 fclose(f);
307 return (size_t)addr - start + offset;
308 }
309 }
310
311 fclose(f);
312 return -EINVAL;
313}
314
315static int
316parse_build_id_buf(const void *note_start, Elf32_Word note_size, char *build_id)
317{
318 Elf32_Word note_offs = 0;
319
320 while (note_offs + sizeof(Elf32_Nhdr) < note_size) {
321 Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs);
322
323 if (nhdr->n_type == 3 && nhdr->n_namesz == sizeof("GNU") &&
324 !strcmp((char *)(nhdr + 1), "GNU") && nhdr->n_descsz > 0 &&
325 nhdr->n_descsz <= BPF_BUILD_ID_SIZE) {
326 memcpy(build_id, note_start + note_offs +
327 ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr), nhdr->n_descsz);
328 memset(build_id + nhdr->n_descsz, 0, BPF_BUILD_ID_SIZE - nhdr->n_descsz);
329 return (int) nhdr->n_descsz;
330 }
331
332 note_offs = note_offs + sizeof(Elf32_Nhdr) +
333 ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4);
334 }
335
336 return -ENOENT;
337}
338
339/* Reads binary from *path* file and returns it in the *build_id* buffer
340 * with *size* which is expected to be at least BPF_BUILD_ID_SIZE bytes.
341 * Returns size of build id on success. On error the error value is
342 * returned.
343 */
344int read_build_id(const char *path, char *build_id, size_t size)
345{
346 int fd, err = -EINVAL;
347 Elf *elf = NULL;
348 GElf_Ehdr ehdr;
349 size_t max, i;
350
351 if (size < BPF_BUILD_ID_SIZE)
352 return -EINVAL;
353
354 fd = open(path, O_RDONLY | O_CLOEXEC);
355 if (fd < 0)
356 return -errno;
357
358 (void)elf_version(EV_CURRENT);
359
360 elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
361 if (!elf)
362 goto out;
363 if (elf_kind(elf) != ELF_K_ELF)
364 goto out;
365 if (!gelf_getehdr(elf, &ehdr))
366 goto out;
367
368 for (i = 0; i < ehdr.e_phnum; i++) {
369 GElf_Phdr mem, *phdr;
370 char *data;
371
372 phdr = gelf_getphdr(elf, i, &mem);
373 if (!phdr)
374 goto out;
375 if (phdr->p_type != PT_NOTE)
376 continue;
377 data = elf_rawfile(elf, &max);
378 if (!data)
379 goto out;
380 if (phdr->p_offset + phdr->p_memsz > max)
381 goto out;
382 err = parse_build_id_buf(data + phdr->p_offset, phdr->p_memsz, build_id);
383 if (err > 0)
384 break;
385 }
386
387out:
388 if (elf)
389 elf_end(elf);
390 close(fd);
391 return err;
392}
393
394int read_trace_pipe_iter(void (*cb)(const char *str, void *data), void *data, int iter)
395{
396 size_t buflen, n;
397 char *buf = NULL;
398 FILE *fp = NULL;
399
400 if (access(TRACEFS_PIPE, F_OK) == 0)
401 fp = fopen(TRACEFS_PIPE, "r");
402 else
403 fp = fopen(DEBUGFS_PIPE, "r");
404 if (!fp)
405 return -1;
406
407 /* We do not want to wait forever when iter is specified. */
408 if (iter)
409 fcntl(fileno(fp), F_SETFL, O_NONBLOCK);
410
411 while ((n = getline(&buf, &buflen, fp) >= 0) || errno == EAGAIN) {
412 if (n > 0)
413 cb(buf, data);
414 if (iter && !(--iter))
415 break;
416 }
417
418 free(buf);
419 if (fp)
420 fclose(fp);
421 return 0;
422}
423
424static void trace_pipe_cb(const char *str, void *data)
425{
426 printf("%s", str);
427}
428
429void read_trace_pipe(void)
430{
431 read_trace_pipe_iter(trace_pipe_cb, NULL, 0);
432}