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