xref: /linux/tools/testing/selftests/bpf/trace_helpers.c (revision 58f6259b7a08f8d47d4629609703d358b042f0fd)
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 }
319