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