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
22 static struct ksym syms[MAX_SYMS];
23 static int sym_cnt;
24 
ksym_cmp(const void * p1,const void * p2)25 static int ksym_cmp(const void *p1, const void *p2)
26 {
27 	return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr;
28 }
29 
load_kallsyms_refresh(void)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 		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 
load_kallsyms(void)62 int 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 
ksym_search(long key)73 struct 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 
ksym_get_addr(const char * name)103 long 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  */
kallsyms_find(const char * sym,unsigned long long * addr)118 int 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 
137 out:
138 	fclose(f);
139 	return err;
140 }
141 
read_trace_pipe(void)142 void 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 
get_uprobe_offset(const void * addr)165 ssize_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 
get_rel_offset(uintptr_t addr)223 ssize_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 
244 static int
parse_build_id_buf(const void * note_start,Elf32_Word note_size,char * build_id)245 parse_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  */
read_build_id(const char * path,char * build_id,size_t size)273 int 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 
316 out:
317 	if (elf)
318 		elf_end(elf);
319 	close(fd);
320 	return err;
321 }
322