Loading...
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 <pthread.h>
11#include <unistd.h>
12#include <linux/perf_event.h>
13#include <linux/fs.h>
14#include <sys/ioctl.h>
15#include <sys/mman.h>
16#include "trace_helpers.h"
17#include <linux/limits.h>
18#include <libelf.h>
19#include <gelf.h>
20#include "bpf/libbpf_internal.h"
21
22#define TRACEFS_PIPE "/sys/kernel/tracing/trace_pipe"
23#define DEBUGFS_PIPE "/sys/kernel/debug/tracing/trace_pipe"
24
25struct ksyms {
26 struct ksym *syms;
27 size_t sym_cap;
28 size_t sym_cnt;
29};
30
31static struct ksyms *ksyms;
32static pthread_mutex_t ksyms_mutex = PTHREAD_MUTEX_INITIALIZER;
33
34static int ksyms__add_symbol(struct ksyms *ksyms, const char *name,
35 unsigned long addr)
36{
37 void *tmp;
38
39 tmp = strdup(name);
40 if (!tmp)
41 return -ENOMEM;
42 ksyms->syms[ksyms->sym_cnt].addr = addr;
43 ksyms->syms[ksyms->sym_cnt].name = tmp;
44 ksyms->sym_cnt++;
45 return 0;
46}
47
48void free_kallsyms_local(struct ksyms *ksyms)
49{
50 unsigned int i;
51
52 if (!ksyms)
53 return;
54
55 if (!ksyms->syms) {
56 free(ksyms);
57 return;
58 }
59
60 for (i = 0; i < ksyms->sym_cnt; i++)
61 free(ksyms->syms[i].name);
62 free(ksyms->syms);
63 free(ksyms);
64}
65
66static struct ksyms *load_kallsyms_local_common(ksym_cmp_t cmp_cb)
67{
68 FILE *f;
69 char func[256], buf[256];
70 char symbol;
71 void *addr;
72 int ret;
73 struct ksyms *ksyms;
74
75 f = fopen("/proc/kallsyms", "r");
76 if (!f)
77 return NULL;
78
79 ksyms = calloc(1, sizeof(struct ksyms));
80 if (!ksyms) {
81 fclose(f);
82 return NULL;
83 }
84
85 while (fgets(buf, sizeof(buf), f)) {
86 if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3)
87 break;
88 if (!addr)
89 continue;
90
91 ret = libbpf_ensure_mem((void **) &ksyms->syms, &ksyms->sym_cap,
92 sizeof(struct ksym), ksyms->sym_cnt + 1);
93 if (ret)
94 goto error;
95 ret = ksyms__add_symbol(ksyms, func, (unsigned long)addr);
96 if (ret)
97 goto error;
98 }
99 fclose(f);
100 qsort(ksyms->syms, ksyms->sym_cnt, sizeof(struct ksym), cmp_cb);
101 return ksyms;
102
103error:
104 fclose(f);
105 free_kallsyms_local(ksyms);
106 return NULL;
107}
108
109static int ksym_cmp(const void *p1, const void *p2)
110{
111 return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr;
112}
113
114struct ksyms *load_kallsyms_local(void)
115{
116 return load_kallsyms_local_common(ksym_cmp);
117}
118
119struct ksyms *load_kallsyms_custom_local(ksym_cmp_t cmp_cb)
120{
121 return load_kallsyms_local_common(cmp_cb);
122}
123
124int load_kallsyms(void)
125{
126 pthread_mutex_lock(&ksyms_mutex);
127 if (!ksyms)
128 ksyms = load_kallsyms_local();
129 pthread_mutex_unlock(&ksyms_mutex);
130 return ksyms ? 0 : 1;
131}
132
133struct ksym *ksym_search_local(struct ksyms *ksyms, long key)
134{
135 int start = 0, end = ksyms->sym_cnt;
136 int result;
137
138 /* kallsyms not loaded. return NULL */
139 if (ksyms->sym_cnt <= 0)
140 return NULL;
141
142 while (start < end) {
143 size_t mid = start + (end - start) / 2;
144
145 result = key - ksyms->syms[mid].addr;
146 if (result < 0)
147 end = mid;
148 else if (result > 0)
149 start = mid + 1;
150 else
151 return &ksyms->syms[mid];
152 }
153
154 if (start >= 1 && ksyms->syms[start - 1].addr < key &&
155 key < ksyms->syms[start].addr)
156 /* valid ksym */
157 return &ksyms->syms[start - 1];
158
159 /* out of range. return _stext */
160 return &ksyms->syms[0];
161}
162
163struct ksym *search_kallsyms_custom_local(struct ksyms *ksyms, const void *p,
164 ksym_search_cmp_t cmp_cb)
165{
166 int start = 0, mid, end = ksyms->sym_cnt;
167 struct ksym *ks;
168 int result;
169
170 while (start < end) {
171 mid = start + (end - start) / 2;
172 ks = &ksyms->syms[mid];
173 result = cmp_cb(p, ks);
174 if (result < 0)
175 end = mid;
176 else if (result > 0)
177 start = mid + 1;
178 else
179 return ks;
180 }
181
182 return NULL;
183}
184
185struct ksym *ksym_search(long key)
186{
187 if (!ksyms)
188 return NULL;
189 return ksym_search_local(ksyms, key);
190}
191
192long ksym_get_addr_local(struct ksyms *ksyms, const char *name)
193{
194 int i;
195
196 for (i = 0; i < ksyms->sym_cnt; i++) {
197 if (strcmp(ksyms->syms[i].name, name) == 0)
198 return ksyms->syms[i].addr;
199 }
200
201 return 0;
202}
203
204long ksym_get_addr(const char *name)
205{
206 if (!ksyms)
207 return 0;
208 return ksym_get_addr_local(ksyms, name);
209}
210
211/* open kallsyms and read symbol addresses on the fly. Without caching all symbols,
212 * this is faster than load + find.
213 */
214int kallsyms_find(const char *sym, unsigned long long *addr)
215{
216 char type, name[500], *match;
217 unsigned long long value;
218 int err = 0;
219 FILE *f;
220
221 f = fopen("/proc/kallsyms", "r");
222 if (!f)
223 return -EINVAL;
224
225 while (fscanf(f, "%llx %c %499s%*[^\n]\n", &value, &type, name) > 0) {
226 /* If CONFIG_LTO_CLANG_THIN is enabled, static variable/function
227 * symbols could be promoted to global due to cross-file inlining.
228 * For such cases, clang compiler will add .llvm.<hash> suffix
229 * to those symbols to avoid potential naming conflict.
230 * Let us ignore .llvm.<hash> suffix during symbol comparison.
231 */
232 if (type == 'd') {
233 match = strstr(name, ".llvm.");
234 if (match)
235 *match = '\0';
236 }
237 if (strcmp(name, sym) == 0) {
238 *addr = value;
239 goto out;
240 }
241 }
242 err = -ENOENT;
243
244out:
245 fclose(f);
246 return err;
247}
248
249#ifdef PROCMAP_QUERY
250int env_verbosity __weak = 0;
251
252static int procmap_query(int fd, const void *addr, __u32 query_flags, size_t *start, size_t *offset, int *flags)
253{
254 char path_buf[PATH_MAX], build_id_buf[20];
255 struct procmap_query q;
256 int err;
257
258 memset(&q, 0, sizeof(q));
259 q.size = sizeof(q);
260 q.query_flags = query_flags;
261 q.query_addr = (__u64)addr;
262 q.vma_name_addr = (__u64)path_buf;
263 q.vma_name_size = sizeof(path_buf);
264 q.build_id_addr = (__u64)build_id_buf;
265 q.build_id_size = sizeof(build_id_buf);
266
267 err = ioctl(fd, PROCMAP_QUERY, &q);
268 if (err < 0) {
269 err = -errno;
270 if (err == -ENOTTY)
271 return -EOPNOTSUPP; /* ioctl() not implemented yet */
272 if (err == -ENOENT)
273 return -ESRCH; /* vma not found */
274 return err;
275 }
276
277 if (env_verbosity >= 1) {
278 printf("VMA FOUND (addr %08lx): %08lx-%08lx %c%c%c%c %08lx %02x:%02x %ld %s (build ID: %s, %d bytes)\n",
279 (long)addr, (long)q.vma_start, (long)q.vma_end,
280 (q.vma_flags & PROCMAP_QUERY_VMA_READABLE) ? 'r' : '-',
281 (q.vma_flags & PROCMAP_QUERY_VMA_WRITABLE) ? 'w' : '-',
282 (q.vma_flags & PROCMAP_QUERY_VMA_EXECUTABLE) ? 'x' : '-',
283 (q.vma_flags & PROCMAP_QUERY_VMA_SHARED) ? 's' : 'p',
284 (long)q.vma_offset, q.dev_major, q.dev_minor, (long)q.inode,
285 q.vma_name_size ? path_buf : "",
286 q.build_id_size ? "YES" : "NO",
287 q.build_id_size);
288 }
289
290 *start = q.vma_start;
291 *offset = q.vma_offset;
292 *flags = q.vma_flags;
293 return 0;
294}
295#else
296# ifndef PROCMAP_QUERY_VMA_EXECUTABLE
297# define PROCMAP_QUERY_VMA_EXECUTABLE 0x04
298# endif
299
300static int procmap_query(int fd, const void *addr, __u32 query_flags, size_t *start, size_t *offset, int *flags)
301{
302 return -EOPNOTSUPP;
303}
304#endif
305
306ssize_t get_uprobe_offset(const void *addr)
307{
308 size_t start, base, end;
309 FILE *f;
310 char buf[256];
311 int err, flags;
312
313 f = fopen("/proc/self/maps", "r");
314 if (!f)
315 return -errno;
316
317 /* requested executable VMA only */
318 err = procmap_query(fileno(f), addr, PROCMAP_QUERY_VMA_EXECUTABLE, &start, &base, &flags);
319 if (err == -EOPNOTSUPP) {
320 bool found = false;
321
322 while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) == 4) {
323 if (buf[2] == 'x' && (uintptr_t)addr >= start && (uintptr_t)addr < end) {
324 found = true;
325 break;
326 }
327 }
328 if (!found) {
329 fclose(f);
330 return -ESRCH;
331 }
332 } else if (err) {
333 fclose(f);
334 return err;
335 }
336 fclose(f);
337
338#if defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2
339
340#define OP_RT_RA_MASK 0xffff0000UL
341#define LIS_R2 0x3c400000UL
342#define ADDIS_R2_R12 0x3c4c0000UL
343#define ADDI_R2_R2 0x38420000UL
344
345 /*
346 * A PPC64 ABIv2 function may have a local and a global entry
347 * point. We need to use the local entry point when patching
348 * functions, so identify and step over the global entry point
349 * sequence.
350 *
351 * The global entry point sequence is always of the form:
352 *
353 * addis r2,r12,XXXX
354 * addi r2,r2,XXXX
355 *
356 * A linker optimisation may convert the addis to lis:
357 *
358 * lis r2,XXXX
359 * addi r2,r2,XXXX
360 */
361 {
362 const __u32 *insn = (const __u32 *)(uintptr_t)addr;
363
364 if ((((*insn & OP_RT_RA_MASK) == ADDIS_R2_R12) ||
365 ((*insn & OP_RT_RA_MASK) == LIS_R2)) &&
366 ((*(insn + 1) & OP_RT_RA_MASK) == ADDI_R2_R2))
367 return (uintptr_t)(insn + 2) - start + base;
368 }
369#endif
370 return (uintptr_t)addr - start + base;
371}
372
373ssize_t get_rel_offset(uintptr_t addr)
374{
375 size_t start, end, offset;
376 char buf[256];
377 FILE *f;
378 int err, flags;
379
380 f = fopen("/proc/self/maps", "r");
381 if (!f)
382 return -errno;
383
384 err = procmap_query(fileno(f), (const void *)addr, 0, &start, &offset, &flags);
385 if (err == 0) {
386 fclose(f);
387 return (size_t)addr - start + offset;
388 } else if (err != -EOPNOTSUPP) {
389 fclose(f);
390 return err;
391 } else if (err) {
392 while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &offset) == 4) {
393 if (addr >= start && addr < end) {
394 fclose(f);
395 return (size_t)addr - start + offset;
396 }
397 }
398 }
399
400 fclose(f);
401 return -EINVAL;
402}
403
404static int
405parse_build_id_buf(const void *note_start, Elf32_Word note_size, char *build_id)
406{
407 Elf32_Word note_offs = 0;
408
409 while (note_offs + sizeof(Elf32_Nhdr) < note_size) {
410 Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs);
411
412 if (nhdr->n_type == 3 && nhdr->n_namesz == sizeof("GNU") &&
413 !strcmp((char *)(nhdr + 1), "GNU") && nhdr->n_descsz > 0 &&
414 nhdr->n_descsz <= BPF_BUILD_ID_SIZE) {
415 memcpy(build_id, note_start + note_offs +
416 ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr), nhdr->n_descsz);
417 memset(build_id + nhdr->n_descsz, 0, BPF_BUILD_ID_SIZE - nhdr->n_descsz);
418 return (int) nhdr->n_descsz;
419 }
420
421 note_offs = note_offs + sizeof(Elf32_Nhdr) +
422 ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4);
423 }
424
425 return -ENOENT;
426}
427
428/* Reads binary from *path* file and returns it in the *build_id* buffer
429 * with *size* which is expected to be at least BPF_BUILD_ID_SIZE bytes.
430 * Returns size of build id on success. On error the error value is
431 * returned.
432 */
433int read_build_id(const char *path, char *build_id, size_t size)
434{
435 int fd, err = -EINVAL;
436 Elf *elf = NULL;
437 GElf_Ehdr ehdr;
438 size_t max, i;
439
440 if (size < BPF_BUILD_ID_SIZE)
441 return -EINVAL;
442
443 fd = open(path, O_RDONLY | O_CLOEXEC);
444 if (fd < 0)
445 return -errno;
446
447 (void)elf_version(EV_CURRENT);
448
449 elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
450 if (!elf)
451 goto out;
452 if (elf_kind(elf) != ELF_K_ELF)
453 goto out;
454 if (!gelf_getehdr(elf, &ehdr))
455 goto out;
456
457 for (i = 0; i < ehdr.e_phnum; i++) {
458 GElf_Phdr mem, *phdr;
459 char *data;
460
461 phdr = gelf_getphdr(elf, i, &mem);
462 if (!phdr)
463 goto out;
464 if (phdr->p_type != PT_NOTE)
465 continue;
466 data = elf_rawfile(elf, &max);
467 if (!data)
468 goto out;
469 if (phdr->p_offset + phdr->p_memsz > max)
470 goto out;
471 err = parse_build_id_buf(data + phdr->p_offset, phdr->p_memsz, build_id);
472 if (err > 0)
473 break;
474 }
475
476out:
477 if (elf)
478 elf_end(elf);
479 close(fd);
480 return err;
481}
482
483int read_trace_pipe_iter(void (*cb)(const char *str, void *data), void *data, int iter)
484{
485 size_t buflen, n;
486 char *buf = NULL;
487 FILE *fp = NULL;
488
489 if (access(TRACEFS_PIPE, F_OK) == 0)
490 fp = fopen(TRACEFS_PIPE, "r");
491 else
492 fp = fopen(DEBUGFS_PIPE, "r");
493 if (!fp)
494 return -1;
495
496 /* We do not want to wait forever when iter is specified. */
497 if (iter)
498 fcntl(fileno(fp), F_SETFL, O_NONBLOCK);
499
500 while ((n = getline(&buf, &buflen, fp) >= 0) || errno == EAGAIN) {
501 if (n > 0)
502 cb(buf, data);
503 if (iter && !(--iter))
504 break;
505 }
506
507 free(buf);
508 if (fp)
509 fclose(fp);
510 return 0;
511}
512
513static void trace_pipe_cb(const char *str, void *data)
514{
515 printf("%s", str);
516}
517
518void read_trace_pipe(void)
519{
520 read_trace_pipe_iter(trace_pipe_cb, NULL, 0);
521}
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 <pthread.h>
11#include <unistd.h>
12#include <linux/perf_event.h>
13#include <sys/mman.h>
14#include "trace_helpers.h"
15#include <linux/limits.h>
16#include <libelf.h>
17#include <gelf.h>
18#include "bpf/libbpf_internal.h"
19
20#define TRACEFS_PIPE "/sys/kernel/tracing/trace_pipe"
21#define DEBUGFS_PIPE "/sys/kernel/debug/tracing/trace_pipe"
22
23struct ksyms {
24 struct ksym *syms;
25 size_t sym_cap;
26 size_t sym_cnt;
27};
28
29static struct ksyms *ksyms;
30static pthread_mutex_t ksyms_mutex = PTHREAD_MUTEX_INITIALIZER;
31
32static int ksyms__add_symbol(struct ksyms *ksyms, const char *name,
33 unsigned long addr)
34{
35 void *tmp;
36
37 tmp = strdup(name);
38 if (!tmp)
39 return -ENOMEM;
40 ksyms->syms[ksyms->sym_cnt].addr = addr;
41 ksyms->syms[ksyms->sym_cnt].name = tmp;
42 ksyms->sym_cnt++;
43 return 0;
44}
45
46void free_kallsyms_local(struct ksyms *ksyms)
47{
48 unsigned int i;
49
50 if (!ksyms)
51 return;
52
53 if (!ksyms->syms) {
54 free(ksyms);
55 return;
56 }
57
58 for (i = 0; i < ksyms->sym_cnt; i++)
59 free(ksyms->syms[i].name);
60 free(ksyms->syms);
61 free(ksyms);
62}
63
64static int ksym_cmp(const void *p1, const void *p2)
65{
66 return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr;
67}
68
69struct ksyms *load_kallsyms_local(void)
70{
71 FILE *f;
72 char func[256], buf[256];
73 char symbol;
74 void *addr;
75 int ret;
76 struct ksyms *ksyms;
77
78 f = fopen("/proc/kallsyms", "r");
79 if (!f)
80 return NULL;
81
82 ksyms = calloc(1, sizeof(struct ksyms));
83 if (!ksyms) {
84 fclose(f);
85 return NULL;
86 }
87
88 while (fgets(buf, sizeof(buf), f)) {
89 if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3)
90 break;
91 if (!addr)
92 continue;
93
94 ret = libbpf_ensure_mem((void **) &ksyms->syms, &ksyms->sym_cap,
95 sizeof(struct ksym), ksyms->sym_cnt + 1);
96 if (ret)
97 goto error;
98 ret = ksyms__add_symbol(ksyms, func, (unsigned long)addr);
99 if (ret)
100 goto error;
101 }
102 fclose(f);
103 qsort(ksyms->syms, ksyms->sym_cnt, sizeof(struct ksym), ksym_cmp);
104 return ksyms;
105
106error:
107 fclose(f);
108 free_kallsyms_local(ksyms);
109 return NULL;
110}
111
112int load_kallsyms(void)
113{
114 pthread_mutex_lock(&ksyms_mutex);
115 if (!ksyms)
116 ksyms = load_kallsyms_local();
117 pthread_mutex_unlock(&ksyms_mutex);
118 return ksyms ? 0 : 1;
119}
120
121struct ksym *ksym_search_local(struct ksyms *ksyms, long key)
122{
123 int start = 0, end = ksyms->sym_cnt;
124 int result;
125
126 /* kallsyms not loaded. return NULL */
127 if (ksyms->sym_cnt <= 0)
128 return NULL;
129
130 while (start < end) {
131 size_t mid = start + (end - start) / 2;
132
133 result = key - ksyms->syms[mid].addr;
134 if (result < 0)
135 end = mid;
136 else if (result > 0)
137 start = mid + 1;
138 else
139 return &ksyms->syms[mid];
140 }
141
142 if (start >= 1 && ksyms->syms[start - 1].addr < key &&
143 key < ksyms->syms[start].addr)
144 /* valid ksym */
145 return &ksyms->syms[start - 1];
146
147 /* out of range. return _stext */
148 return &ksyms->syms[0];
149}
150
151struct ksym *ksym_search(long key)
152{
153 if (!ksyms)
154 return NULL;
155 return ksym_search_local(ksyms, key);
156}
157
158long ksym_get_addr_local(struct ksyms *ksyms, const char *name)
159{
160 int i;
161
162 for (i = 0; i < ksyms->sym_cnt; i++) {
163 if (strcmp(ksyms->syms[i].name, name) == 0)
164 return ksyms->syms[i].addr;
165 }
166
167 return 0;
168}
169
170long ksym_get_addr(const char *name)
171{
172 if (!ksyms)
173 return 0;
174 return ksym_get_addr_local(ksyms, name);
175}
176
177/* open kallsyms and read symbol addresses on the fly. Without caching all symbols,
178 * this is faster than load + find.
179 */
180int kallsyms_find(const char *sym, unsigned long long *addr)
181{
182 char type, name[500];
183 unsigned long long value;
184 int err = 0;
185 FILE *f;
186
187 f = fopen("/proc/kallsyms", "r");
188 if (!f)
189 return -EINVAL;
190
191 while (fscanf(f, "%llx %c %499s%*[^\n]\n", &value, &type, name) > 0) {
192 if (strcmp(name, sym) == 0) {
193 *addr = value;
194 goto out;
195 }
196 }
197 err = -ENOENT;
198
199out:
200 fclose(f);
201 return err;
202}
203
204void read_trace_pipe(void)
205{
206 int trace_fd;
207
208 if (access(TRACEFS_PIPE, F_OK) == 0)
209 trace_fd = open(TRACEFS_PIPE, O_RDONLY, 0);
210 else
211 trace_fd = open(DEBUGFS_PIPE, O_RDONLY, 0);
212 if (trace_fd < 0)
213 return;
214
215 while (1) {
216 static char buf[4096];
217 ssize_t sz;
218
219 sz = read(trace_fd, buf, sizeof(buf) - 1);
220 if (sz > 0) {
221 buf[sz] = 0;
222 puts(buf);
223 }
224 }
225}
226
227ssize_t get_uprobe_offset(const void *addr)
228{
229 size_t start, end, base;
230 char buf[256];
231 bool found = false;
232 FILE *f;
233
234 f = fopen("/proc/self/maps", "r");
235 if (!f)
236 return -errno;
237
238 while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) == 4) {
239 if (buf[2] == 'x' && (uintptr_t)addr >= start && (uintptr_t)addr < end) {
240 found = true;
241 break;
242 }
243 }
244
245 fclose(f);
246
247 if (!found)
248 return -ESRCH;
249
250#if defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2
251
252#define OP_RT_RA_MASK 0xffff0000UL
253#define LIS_R2 0x3c400000UL
254#define ADDIS_R2_R12 0x3c4c0000UL
255#define ADDI_R2_R2 0x38420000UL
256
257 /*
258 * A PPC64 ABIv2 function may have a local and a global entry
259 * point. We need to use the local entry point when patching
260 * functions, so identify and step over the global entry point
261 * sequence.
262 *
263 * The global entry point sequence is always of the form:
264 *
265 * addis r2,r12,XXXX
266 * addi r2,r2,XXXX
267 *
268 * A linker optimisation may convert the addis to lis:
269 *
270 * lis r2,XXXX
271 * addi r2,r2,XXXX
272 */
273 {
274 const __u32 *insn = (const __u32 *)(uintptr_t)addr;
275
276 if ((((*insn & OP_RT_RA_MASK) == ADDIS_R2_R12) ||
277 ((*insn & OP_RT_RA_MASK) == LIS_R2)) &&
278 ((*(insn + 1) & OP_RT_RA_MASK) == ADDI_R2_R2))
279 return (uintptr_t)(insn + 2) - start + base;
280 }
281#endif
282 return (uintptr_t)addr - start + base;
283}
284
285ssize_t get_rel_offset(uintptr_t addr)
286{
287 size_t start, end, offset;
288 char buf[256];
289 FILE *f;
290
291 f = fopen("/proc/self/maps", "r");
292 if (!f)
293 return -errno;
294
295 while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &offset) == 4) {
296 if (addr >= start && addr < end) {
297 fclose(f);
298 return (size_t)addr - start + offset;
299 }
300 }
301
302 fclose(f);
303 return -EINVAL;
304}
305
306static int
307parse_build_id_buf(const void *note_start, Elf32_Word note_size, char *build_id)
308{
309 Elf32_Word note_offs = 0;
310
311 while (note_offs + sizeof(Elf32_Nhdr) < note_size) {
312 Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs);
313
314 if (nhdr->n_type == 3 && nhdr->n_namesz == sizeof("GNU") &&
315 !strcmp((char *)(nhdr + 1), "GNU") && nhdr->n_descsz > 0 &&
316 nhdr->n_descsz <= BPF_BUILD_ID_SIZE) {
317 memcpy(build_id, note_start + note_offs +
318 ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr), nhdr->n_descsz);
319 memset(build_id + nhdr->n_descsz, 0, BPF_BUILD_ID_SIZE - nhdr->n_descsz);
320 return (int) nhdr->n_descsz;
321 }
322
323 note_offs = note_offs + sizeof(Elf32_Nhdr) +
324 ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4);
325 }
326
327 return -ENOENT;
328}
329
330/* Reads binary from *path* file and returns it in the *build_id* buffer
331 * with *size* which is expected to be at least BPF_BUILD_ID_SIZE bytes.
332 * Returns size of build id on success. On error the error value is
333 * returned.
334 */
335int read_build_id(const char *path, char *build_id, size_t size)
336{
337 int fd, err = -EINVAL;
338 Elf *elf = NULL;
339 GElf_Ehdr ehdr;
340 size_t max, i;
341
342 if (size < BPF_BUILD_ID_SIZE)
343 return -EINVAL;
344
345 fd = open(path, O_RDONLY | O_CLOEXEC);
346 if (fd < 0)
347 return -errno;
348
349 (void)elf_version(EV_CURRENT);
350
351 elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
352 if (!elf)
353 goto out;
354 if (elf_kind(elf) != ELF_K_ELF)
355 goto out;
356 if (!gelf_getehdr(elf, &ehdr))
357 goto out;
358
359 for (i = 0; i < ehdr.e_phnum; i++) {
360 GElf_Phdr mem, *phdr;
361 char *data;
362
363 phdr = gelf_getphdr(elf, i, &mem);
364 if (!phdr)
365 goto out;
366 if (phdr->p_type != PT_NOTE)
367 continue;
368 data = elf_rawfile(elf, &max);
369 if (!data)
370 goto out;
371 if (phdr->p_offset + phdr->p_memsz > max)
372 goto out;
373 err = parse_build_id_buf(data + phdr->p_offset, phdr->p_memsz, build_id);
374 if (err > 0)
375 break;
376 }
377
378out:
379 if (elf)
380 elf_end(elf);
381 close(fd);
382 return err;
383}