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
22static struct ksym syms[MAX_SYMS];
23static int sym_cnt;
24
25static int ksym_cmp(const void *p1, const void *p2)
26{
27	return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr;
28}
29
30int 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
62int 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
73struct 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
103long 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 */
118int 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
137out:
138	fclose(f);
139	return err;
140}
141
142void 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
165ssize_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
223ssize_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
244static int
245parse_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 */
273int 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
316out:
317	if (elf)
318		elf_end(elf);
319	close(fd);
320	return err;
321}
322