xref: /openbmc/linux/tools/testing/selftests/bpf/trace_helpers.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
128dbf861SYonghong Song // SPDX-License-Identifier: GPL-2.0
2025bd7c7SSong Liu #include <ctype.h>
328dbf861SYonghong Song #include <stdio.h>
428dbf861SYonghong Song #include <stdlib.h>
528dbf861SYonghong Song #include <string.h>
628dbf861SYonghong Song #include <assert.h>
728dbf861SYonghong Song #include <errno.h>
824a6034aSDaniel T. Lee #include <fcntl.h>
928dbf861SYonghong Song #include <poll.h>
1028dbf861SYonghong Song #include <unistd.h>
1128dbf861SYonghong Song #include <linux/perf_event.h>
1228dbf861SYonghong Song #include <sys/mman.h>
1328dbf861SYonghong Song #include "trace_helpers.h"
1488dc8b36SJiri Olsa #include <linux/limits.h>
1588dc8b36SJiri Olsa #include <libelf.h>
1688dc8b36SJiri Olsa #include <gelf.h>
1728dbf861SYonghong Song 
18ab4c15feSRoss Zwisler #define TRACEFS_PIPE	"/sys/kernel/tracing/trace_pipe"
19ab4c15feSRoss Zwisler #define DEBUGFS_PIPE	"/sys/kernel/debug/tracing/trace_pipe"
2024a6034aSDaniel T. Lee 
21*e76a0143SBjörn Töpel #define MAX_SYMS 400000
2228dbf861SYonghong Song static struct ksym syms[MAX_SYMS];
2328dbf861SYonghong Song static int sym_cnt;
2428dbf861SYonghong Song 
ksym_cmp(const void * p1,const void * p2)2528dbf861SYonghong Song static int ksym_cmp(const void *p1, const void *p2)
2628dbf861SYonghong Song {
2728dbf861SYonghong Song 	return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr;
2828dbf861SYonghong Song }
2928dbf861SYonghong Song 
load_kallsyms_refresh(void)3010705b2bSJiri Olsa int load_kallsyms_refresh(void)
3128dbf861SYonghong Song {
322d0df019SYuntao Wang 	FILE *f;
3328dbf861SYonghong Song 	char func[256], buf[256];
3428dbf861SYonghong Song 	char symbol;
3528dbf861SYonghong Song 	void *addr;
3628dbf861SYonghong Song 	int i = 0;
3728dbf861SYonghong Song 
3810705b2bSJiri Olsa 	sym_cnt = 0;
39f7a11eecSJiri Olsa 
402d0df019SYuntao Wang 	f = fopen("/proc/kallsyms", "r");
412d0df019SYuntao Wang 	if (!f)
422d0df019SYuntao Wang 		return -ENOENT;
432d0df019SYuntao Wang 
4492bd6820SChang-Hsien Tsai 	while (fgets(buf, sizeof(buf), f)) {
4528dbf861SYonghong Song 		if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3)
4628dbf861SYonghong Song 			break;
4728dbf861SYonghong Song 		if (!addr)
4828dbf861SYonghong Song 			continue;
49*e76a0143SBjörn Töpel 		if (i >= MAX_SYMS)
50*e76a0143SBjörn Töpel 			return -EFBIG;
51*e76a0143SBjörn Töpel 
5228dbf861SYonghong Song 		syms[i].addr = (long) addr;
5328dbf861SYonghong Song 		syms[i].name = strdup(func);
5428dbf861SYonghong Song 		i++;
5528dbf861SYonghong Song 	}
561bd70d2eSPeng Hao 	fclose(f);
5728dbf861SYonghong Song 	sym_cnt = i;
5828dbf861SYonghong Song 	qsort(syms, sym_cnt, sizeof(struct ksym), ksym_cmp);
5928dbf861SYonghong Song 	return 0;
6028dbf861SYonghong Song }
6128dbf861SYonghong Song 
load_kallsyms(void)6210705b2bSJiri Olsa int load_kallsyms(void)
6310705b2bSJiri Olsa {
6410705b2bSJiri Olsa 	/*
6510705b2bSJiri Olsa 	 * This is called/used from multiplace places,
6610705b2bSJiri Olsa 	 * load symbols just once.
6710705b2bSJiri Olsa 	 */
6810705b2bSJiri Olsa 	if (sym_cnt)
6910705b2bSJiri Olsa 		return 0;
7010705b2bSJiri Olsa 	return load_kallsyms_refresh();
7110705b2bSJiri Olsa }
7210705b2bSJiri Olsa 
ksym_search(long key)7328dbf861SYonghong Song struct ksym *ksym_search(long key)
7428dbf861SYonghong Song {
7528dbf861SYonghong Song 	int start = 0, end = sym_cnt;
7628dbf861SYonghong Song 	int result;
7728dbf861SYonghong Song 
780979ff79SDaniel T. Lee 	/* kallsyms not loaded. return NULL */
790979ff79SDaniel T. Lee 	if (sym_cnt <= 0)
800979ff79SDaniel T. Lee 		return NULL;
810979ff79SDaniel T. Lee 
8228dbf861SYonghong Song 	while (start < end) {
8328dbf861SYonghong Song 		size_t mid = start + (end - start) / 2;
8428dbf861SYonghong Song 
8528dbf861SYonghong Song 		result = key - syms[mid].addr;
8628dbf861SYonghong Song 		if (result < 0)
8728dbf861SYonghong Song 			end = mid;
8828dbf861SYonghong Song 		else if (result > 0)
8928dbf861SYonghong Song 			start = mid + 1;
9028dbf861SYonghong Song 		else
9128dbf861SYonghong Song 			return &syms[mid];
9228dbf861SYonghong Song 	}
9328dbf861SYonghong Song 
9428dbf861SYonghong Song 	if (start >= 1 && syms[start - 1].addr < key &&
9528dbf861SYonghong Song 	    key < syms[start].addr)
9628dbf861SYonghong Song 		/* valid ksym */
9728dbf861SYonghong Song 		return &syms[start - 1];
9828dbf861SYonghong Song 
9928dbf861SYonghong Song 	/* out of range. return _stext */
10028dbf861SYonghong Song 	return &syms[0];
10128dbf861SYonghong Song }
10228dbf861SYonghong Song 
ksym_get_addr(const char * name)10373bc4d9fSYonghong Song long ksym_get_addr(const char *name)
10473bc4d9fSYonghong Song {
10573bc4d9fSYonghong Song 	int i;
10673bc4d9fSYonghong Song 
10773bc4d9fSYonghong Song 	for (i = 0; i < sym_cnt; i++) {
10873bc4d9fSYonghong Song 		if (strcmp(syms[i].name, name) == 0)
10973bc4d9fSYonghong Song 			return syms[i].addr;
11073bc4d9fSYonghong Song 	}
11173bc4d9fSYonghong Song 
11273bc4d9fSYonghong Song 	return 0;
11373bc4d9fSYonghong Song }
11424a6034aSDaniel T. Lee 
1152c2f6abeSHao Luo /* open kallsyms and read symbol addresses on the fly. Without caching all symbols,
1162c2f6abeSHao Luo  * this is faster than load + find.
1172c2f6abeSHao Luo  */
kallsyms_find(const char * sym,unsigned long long * addr)1182c2f6abeSHao Luo int kallsyms_find(const char *sym, unsigned long long *addr)
1192c2f6abeSHao Luo {
1202c2f6abeSHao Luo 	char type, name[500];
1212c2f6abeSHao Luo 	unsigned long long value;
1222c2f6abeSHao Luo 	int err = 0;
1232c2f6abeSHao Luo 	FILE *f;
1242c2f6abeSHao Luo 
1252c2f6abeSHao Luo 	f = fopen("/proc/kallsyms", "r");
1262c2f6abeSHao Luo 	if (!f)
1272c2f6abeSHao Luo 		return -EINVAL;
1282c2f6abeSHao Luo 
1292c2f6abeSHao Luo 	while (fscanf(f, "%llx %c %499s%*[^\n]\n", &value, &type, name) > 0) {
1302c2f6abeSHao Luo 		if (strcmp(name, sym) == 0) {
1312c2f6abeSHao Luo 			*addr = value;
1322c2f6abeSHao Luo 			goto out;
1332c2f6abeSHao Luo 		}
1342c2f6abeSHao Luo 	}
1352c2f6abeSHao Luo 	err = -ENOENT;
1362c2f6abeSHao Luo 
1372c2f6abeSHao Luo out:
1382c2f6abeSHao Luo 	fclose(f);
1392c2f6abeSHao Luo 	return err;
1402c2f6abeSHao Luo }
1412c2f6abeSHao Luo 
read_trace_pipe(void)14224a6034aSDaniel T. Lee void read_trace_pipe(void)
14324a6034aSDaniel T. Lee {
14424a6034aSDaniel T. Lee 	int trace_fd;
14524a6034aSDaniel T. Lee 
146ab4c15feSRoss Zwisler 	if (access(TRACEFS_PIPE, F_OK) == 0)
147ab4c15feSRoss Zwisler 		trace_fd = open(TRACEFS_PIPE, O_RDONLY, 0);
148ab4c15feSRoss Zwisler 	else
149ab4c15feSRoss Zwisler 		trace_fd = open(DEBUGFS_PIPE, O_RDONLY, 0);
15024a6034aSDaniel T. Lee 	if (trace_fd < 0)
15124a6034aSDaniel T. Lee 		return;
15224a6034aSDaniel T. Lee 
15324a6034aSDaniel T. Lee 	while (1) {
15424a6034aSDaniel T. Lee 		static char buf[4096];
15524a6034aSDaniel T. Lee 		ssize_t sz;
15624a6034aSDaniel T. Lee 
15724a6034aSDaniel T. Lee 		sz = read(trace_fd, buf, sizeof(buf) - 1);
15824a6034aSDaniel T. Lee 		if (sz > 0) {
15924a6034aSDaniel T. Lee 			buf[sz] = 0;
16024a6034aSDaniel T. Lee 			puts(buf);
16124a6034aSDaniel T. Lee 		}
16224a6034aSDaniel T. Lee 	}
16324a6034aSDaniel T. Lee }
164a549aaa6SAndrii Nakryiko 
get_uprobe_offset(const void * addr)165ff943683SAndrii Nakryiko ssize_t get_uprobe_offset(const void *addr)
166ff943683SAndrii Nakryiko {
167ff943683SAndrii Nakryiko 	size_t start, end, base;
168ff943683SAndrii Nakryiko 	char buf[256];
169cdb5ed97SYonghong Song 	bool found = false;
170ff943683SAndrii Nakryiko 	FILE *f;
171ff943683SAndrii Nakryiko 
172ff943683SAndrii Nakryiko 	f = fopen("/proc/self/maps", "r");
173ff943683SAndrii Nakryiko 	if (!f)
174ff943683SAndrii Nakryiko 		return -errno;
175ff943683SAndrii Nakryiko 
176ff943683SAndrii Nakryiko 	while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) == 4) {
177ff943683SAndrii Nakryiko 		if (buf[2] == 'x' && (uintptr_t)addr >= start && (uintptr_t)addr < end) {
178ff943683SAndrii Nakryiko 			found = true;
179ff943683SAndrii Nakryiko 			break;
180ff943683SAndrii Nakryiko 		}
181ff943683SAndrii Nakryiko 	}
182ff943683SAndrii Nakryiko 
183ff943683SAndrii Nakryiko 	fclose(f);
184ff943683SAndrii Nakryiko 
185ff943683SAndrii Nakryiko 	if (!found)
186ff943683SAndrii Nakryiko 		return -ESRCH;
187ff943683SAndrii Nakryiko 
188a549aaa6SAndrii Nakryiko #if defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2
189a549aaa6SAndrii Nakryiko 
190a549aaa6SAndrii Nakryiko #define OP_RT_RA_MASK   0xffff0000UL
191a549aaa6SAndrii Nakryiko #define LIS_R2          0x3c400000UL
192a549aaa6SAndrii Nakryiko #define ADDIS_R2_R12    0x3c4c0000UL
193a549aaa6SAndrii Nakryiko #define ADDI_R2_R2      0x38420000UL
194a549aaa6SAndrii Nakryiko 
195a549aaa6SAndrii Nakryiko 	/*
196a549aaa6SAndrii Nakryiko 	 * A PPC64 ABIv2 function may have a local and a global entry
197a549aaa6SAndrii Nakryiko 	 * point. We need to use the local entry point when patching
198a549aaa6SAndrii Nakryiko 	 * functions, so identify and step over the global entry point
199a549aaa6SAndrii Nakryiko 	 * sequence.
200a549aaa6SAndrii Nakryiko 	 *
201a549aaa6SAndrii Nakryiko 	 * The global entry point sequence is always of the form:
202a549aaa6SAndrii Nakryiko 	 *
203a549aaa6SAndrii Nakryiko 	 * addis r2,r12,XXXX
204a549aaa6SAndrii Nakryiko 	 * addi  r2,r2,XXXX
205a549aaa6SAndrii Nakryiko 	 *
206a549aaa6SAndrii Nakryiko 	 * A linker optimisation may convert the addis to lis:
207a549aaa6SAndrii Nakryiko 	 *
208a549aaa6SAndrii Nakryiko 	 * lis   r2,XXXX
209a549aaa6SAndrii Nakryiko 	 * addi  r2,r2,XXXX
210a549aaa6SAndrii Nakryiko 	 */
211ff943683SAndrii Nakryiko 	{
212ff943683SAndrii Nakryiko 		const u32 *insn = (const u32 *)(uintptr_t)addr;
213ff943683SAndrii Nakryiko 
214a549aaa6SAndrii Nakryiko 		if ((((*insn & OP_RT_RA_MASK) == ADDIS_R2_R12) ||
215a549aaa6SAndrii Nakryiko 		     ((*insn & OP_RT_RA_MASK) == LIS_R2)) &&
216a549aaa6SAndrii Nakryiko 		    ((*(insn + 1) & OP_RT_RA_MASK) == ADDI_R2_R2))
217ff943683SAndrii Nakryiko 			return (uintptr_t)(insn + 2) - start + base;
218a549aaa6SAndrii Nakryiko 	}
219a549aaa6SAndrii Nakryiko #endif
220ff943683SAndrii Nakryiko 	return (uintptr_t)addr - start + base;
221a549aaa6SAndrii Nakryiko }
2224bd11e08SAndrii Nakryiko 
get_rel_offset(uintptr_t addr)2234bd11e08SAndrii Nakryiko ssize_t get_rel_offset(uintptr_t addr)
2244bd11e08SAndrii Nakryiko {
2254bd11e08SAndrii Nakryiko 	size_t start, end, offset;
2264bd11e08SAndrii Nakryiko 	char buf[256];
2274bd11e08SAndrii Nakryiko 	FILE *f;
2284bd11e08SAndrii Nakryiko 
2294bd11e08SAndrii Nakryiko 	f = fopen("/proc/self/maps", "r");
2304bd11e08SAndrii Nakryiko 	if (!f)
2314bd11e08SAndrii Nakryiko 		return -errno;
2324bd11e08SAndrii Nakryiko 
2334bd11e08SAndrii Nakryiko 	while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &offset) == 4) {
2344bd11e08SAndrii Nakryiko 		if (addr >= start && addr < end) {
2354bd11e08SAndrii Nakryiko 			fclose(f);
2364bd11e08SAndrii Nakryiko 			return (size_t)addr - start + offset;
2374bd11e08SAndrii Nakryiko 		}
2384bd11e08SAndrii Nakryiko 	}
2394bd11e08SAndrii Nakryiko 
2404bd11e08SAndrii Nakryiko 	fclose(f);
2414bd11e08SAndrii Nakryiko 	return -EINVAL;
2424bd11e08SAndrii Nakryiko }
24388dc8b36SJiri Olsa 
24488dc8b36SJiri Olsa static int
parse_build_id_buf(const void * note_start,Elf32_Word note_size,char * build_id)24588dc8b36SJiri Olsa parse_build_id_buf(const void *note_start, Elf32_Word note_size, char *build_id)
24688dc8b36SJiri Olsa {
24788dc8b36SJiri Olsa 	Elf32_Word note_offs = 0;
24888dc8b36SJiri Olsa 
24988dc8b36SJiri Olsa 	while (note_offs + sizeof(Elf32_Nhdr) < note_size) {
25088dc8b36SJiri Olsa 		Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs);
25188dc8b36SJiri Olsa 
25288dc8b36SJiri Olsa 		if (nhdr->n_type == 3 && nhdr->n_namesz == sizeof("GNU") &&
25388dc8b36SJiri Olsa 		    !strcmp((char *)(nhdr + 1), "GNU") && nhdr->n_descsz > 0 &&
25488dc8b36SJiri Olsa 		    nhdr->n_descsz <= BPF_BUILD_ID_SIZE) {
25588dc8b36SJiri Olsa 			memcpy(build_id, note_start + note_offs +
25688dc8b36SJiri Olsa 			       ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr), nhdr->n_descsz);
25788dc8b36SJiri Olsa 			memset(build_id + nhdr->n_descsz, 0, BPF_BUILD_ID_SIZE - nhdr->n_descsz);
25888dc8b36SJiri Olsa 			return (int) nhdr->n_descsz;
25988dc8b36SJiri Olsa 		}
26088dc8b36SJiri Olsa 
26188dc8b36SJiri Olsa 		note_offs = note_offs + sizeof(Elf32_Nhdr) +
26288dc8b36SJiri Olsa 			   ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4);
26388dc8b36SJiri Olsa 	}
26488dc8b36SJiri Olsa 
26588dc8b36SJiri Olsa 	return -ENOENT;
26688dc8b36SJiri Olsa }
26788dc8b36SJiri Olsa 
26888dc8b36SJiri Olsa /* Reads binary from *path* file and returns it in the *build_id* buffer
26988dc8b36SJiri Olsa  * with *size* which is expected to be at least BPF_BUILD_ID_SIZE bytes.
27088dc8b36SJiri Olsa  * Returns size of build id on success. On error the error value is
27188dc8b36SJiri Olsa  * returned.
27288dc8b36SJiri Olsa  */
read_build_id(const char * path,char * build_id,size_t size)27388dc8b36SJiri Olsa int read_build_id(const char *path, char *build_id, size_t size)
27488dc8b36SJiri Olsa {
27588dc8b36SJiri Olsa 	int fd, err = -EINVAL;
27688dc8b36SJiri Olsa 	Elf *elf = NULL;
27788dc8b36SJiri Olsa 	GElf_Ehdr ehdr;
27888dc8b36SJiri Olsa 	size_t max, i;
27988dc8b36SJiri Olsa 
28088dc8b36SJiri Olsa 	if (size < BPF_BUILD_ID_SIZE)
28188dc8b36SJiri Olsa 		return -EINVAL;
28288dc8b36SJiri Olsa 
28388dc8b36SJiri Olsa 	fd = open(path, O_RDONLY | O_CLOEXEC);
28488dc8b36SJiri Olsa 	if (fd < 0)
28588dc8b36SJiri Olsa 		return -errno;
28688dc8b36SJiri Olsa 
28788dc8b36SJiri Olsa 	(void)elf_version(EV_CURRENT);
28888dc8b36SJiri Olsa 
28988dc8b36SJiri Olsa 	elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
29088dc8b36SJiri Olsa 	if (!elf)
29188dc8b36SJiri Olsa 		goto out;
29288dc8b36SJiri Olsa 	if (elf_kind(elf) != ELF_K_ELF)
29388dc8b36SJiri Olsa 		goto out;
29488dc8b36SJiri Olsa 	if (!gelf_getehdr(elf, &ehdr))
29588dc8b36SJiri Olsa 		goto out;
29688dc8b36SJiri Olsa 
29788dc8b36SJiri Olsa 	for (i = 0; i < ehdr.e_phnum; i++) {
29888dc8b36SJiri Olsa 		GElf_Phdr mem, *phdr;
29988dc8b36SJiri Olsa 		char *data;
30088dc8b36SJiri Olsa 
30188dc8b36SJiri Olsa 		phdr = gelf_getphdr(elf, i, &mem);
30288dc8b36SJiri Olsa 		if (!phdr)
30388dc8b36SJiri Olsa 			goto out;
30488dc8b36SJiri Olsa 		if (phdr->p_type != PT_NOTE)
30588dc8b36SJiri Olsa 			continue;
30688dc8b36SJiri Olsa 		data = elf_rawfile(elf, &max);
30788dc8b36SJiri Olsa 		if (!data)
30888dc8b36SJiri Olsa 			goto out;
30988dc8b36SJiri Olsa 		if (phdr->p_offset + phdr->p_memsz > max)
31088dc8b36SJiri Olsa 			goto out;
31188dc8b36SJiri Olsa 		err = parse_build_id_buf(data + phdr->p_offset, phdr->p_memsz, build_id);
31288dc8b36SJiri Olsa 		if (err > 0)
31388dc8b36SJiri Olsa 			break;
31488dc8b36SJiri Olsa 	}
31588dc8b36SJiri Olsa 
31688dc8b36SJiri Olsa out:
31788dc8b36SJiri Olsa 	if (elf)
31888dc8b36SJiri Olsa 		elf_end(elf);
31988dc8b36SJiri Olsa 	close(fd);
32088dc8b36SJiri Olsa 	return err;
32188dc8b36SJiri Olsa }
322