162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <ctype.h> 362306a36Sopenharmony_ci#include <stdio.h> 462306a36Sopenharmony_ci#include <stdlib.h> 562306a36Sopenharmony_ci#include <string.h> 662306a36Sopenharmony_ci#include <assert.h> 762306a36Sopenharmony_ci#include <errno.h> 862306a36Sopenharmony_ci#include <fcntl.h> 962306a36Sopenharmony_ci#include <poll.h> 1062306a36Sopenharmony_ci#include <unistd.h> 1162306a36Sopenharmony_ci#include <linux/perf_event.h> 1262306a36Sopenharmony_ci#include <sys/mman.h> 1362306a36Sopenharmony_ci#include "trace_helpers.h" 1462306a36Sopenharmony_ci#include <linux/limits.h> 1562306a36Sopenharmony_ci#include <libelf.h> 1662306a36Sopenharmony_ci#include <gelf.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define TRACEFS_PIPE "/sys/kernel/tracing/trace_pipe" 1962306a36Sopenharmony_ci#define DEBUGFS_PIPE "/sys/kernel/debug/tracing/trace_pipe" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define MAX_SYMS 400000 2262306a36Sopenharmony_cistatic struct ksym syms[MAX_SYMS]; 2362306a36Sopenharmony_cistatic int sym_cnt; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int ksym_cmp(const void *p1, const void *p2) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr; 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ciint load_kallsyms_refresh(void) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci FILE *f; 3362306a36Sopenharmony_ci char func[256], buf[256]; 3462306a36Sopenharmony_ci char symbol; 3562306a36Sopenharmony_ci void *addr; 3662306a36Sopenharmony_ci int i = 0; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci sym_cnt = 0; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci f = fopen("/proc/kallsyms", "r"); 4162306a36Sopenharmony_ci if (!f) 4262306a36Sopenharmony_ci return -ENOENT; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci while (fgets(buf, sizeof(buf), f)) { 4562306a36Sopenharmony_ci if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3) 4662306a36Sopenharmony_ci break; 4762306a36Sopenharmony_ci if (!addr) 4862306a36Sopenharmony_ci continue; 4962306a36Sopenharmony_ci if (i >= MAX_SYMS) 5062306a36Sopenharmony_ci return -EFBIG; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci syms[i].addr = (long) addr; 5362306a36Sopenharmony_ci syms[i].name = strdup(func); 5462306a36Sopenharmony_ci i++; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci fclose(f); 5762306a36Sopenharmony_ci sym_cnt = i; 5862306a36Sopenharmony_ci qsort(syms, sym_cnt, sizeof(struct ksym), ksym_cmp); 5962306a36Sopenharmony_ci return 0; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ciint load_kallsyms(void) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci /* 6562306a36Sopenharmony_ci * This is called/used from multiplace places, 6662306a36Sopenharmony_ci * load symbols just once. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci if (sym_cnt) 6962306a36Sopenharmony_ci return 0; 7062306a36Sopenharmony_ci return load_kallsyms_refresh(); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistruct ksym *ksym_search(long key) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci int start = 0, end = sym_cnt; 7662306a36Sopenharmony_ci int result; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* kallsyms not loaded. return NULL */ 7962306a36Sopenharmony_ci if (sym_cnt <= 0) 8062306a36Sopenharmony_ci return NULL; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci while (start < end) { 8362306a36Sopenharmony_ci size_t mid = start + (end - start) / 2; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci result = key - syms[mid].addr; 8662306a36Sopenharmony_ci if (result < 0) 8762306a36Sopenharmony_ci end = mid; 8862306a36Sopenharmony_ci else if (result > 0) 8962306a36Sopenharmony_ci start = mid + 1; 9062306a36Sopenharmony_ci else 9162306a36Sopenharmony_ci return &syms[mid]; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (start >= 1 && syms[start - 1].addr < key && 9562306a36Sopenharmony_ci key < syms[start].addr) 9662306a36Sopenharmony_ci /* valid ksym */ 9762306a36Sopenharmony_ci return &syms[start - 1]; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* out of range. return _stext */ 10062306a36Sopenharmony_ci return &syms[0]; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cilong ksym_get_addr(const char *name) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci int i; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci for (i = 0; i < sym_cnt; i++) { 10862306a36Sopenharmony_ci if (strcmp(syms[i].name, name) == 0) 10962306a36Sopenharmony_ci return syms[i].addr; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return 0; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* open kallsyms and read symbol addresses on the fly. Without caching all symbols, 11662306a36Sopenharmony_ci * this is faster than load + find. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ciint kallsyms_find(const char *sym, unsigned long long *addr) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci char type, name[500]; 12162306a36Sopenharmony_ci unsigned long long value; 12262306a36Sopenharmony_ci int err = 0; 12362306a36Sopenharmony_ci FILE *f; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci f = fopen("/proc/kallsyms", "r"); 12662306a36Sopenharmony_ci if (!f) 12762306a36Sopenharmony_ci return -EINVAL; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci while (fscanf(f, "%llx %c %499s%*[^\n]\n", &value, &type, name) > 0) { 13062306a36Sopenharmony_ci if (strcmp(name, sym) == 0) { 13162306a36Sopenharmony_ci *addr = value; 13262306a36Sopenharmony_ci goto out; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci err = -ENOENT; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ciout: 13862306a36Sopenharmony_ci fclose(f); 13962306a36Sopenharmony_ci return err; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_civoid read_trace_pipe(void) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci int trace_fd; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (access(TRACEFS_PIPE, F_OK) == 0) 14762306a36Sopenharmony_ci trace_fd = open(TRACEFS_PIPE, O_RDONLY, 0); 14862306a36Sopenharmony_ci else 14962306a36Sopenharmony_ci trace_fd = open(DEBUGFS_PIPE, O_RDONLY, 0); 15062306a36Sopenharmony_ci if (trace_fd < 0) 15162306a36Sopenharmony_ci return; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci while (1) { 15462306a36Sopenharmony_ci static char buf[4096]; 15562306a36Sopenharmony_ci ssize_t sz; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci sz = read(trace_fd, buf, sizeof(buf) - 1); 15862306a36Sopenharmony_ci if (sz > 0) { 15962306a36Sopenharmony_ci buf[sz] = 0; 16062306a36Sopenharmony_ci puts(buf); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cissize_t get_uprobe_offset(const void *addr) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci size_t start, end, base; 16862306a36Sopenharmony_ci char buf[256]; 16962306a36Sopenharmony_ci bool found = false; 17062306a36Sopenharmony_ci FILE *f; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci f = fopen("/proc/self/maps", "r"); 17362306a36Sopenharmony_ci if (!f) 17462306a36Sopenharmony_ci return -errno; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) == 4) { 17762306a36Sopenharmony_ci if (buf[2] == 'x' && (uintptr_t)addr >= start && (uintptr_t)addr < end) { 17862306a36Sopenharmony_ci found = true; 17962306a36Sopenharmony_ci break; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci fclose(f); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (!found) 18662306a36Sopenharmony_ci return -ESRCH; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci#if defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci#define OP_RT_RA_MASK 0xffff0000UL 19162306a36Sopenharmony_ci#define LIS_R2 0x3c400000UL 19262306a36Sopenharmony_ci#define ADDIS_R2_R12 0x3c4c0000UL 19362306a36Sopenharmony_ci#define ADDI_R2_R2 0x38420000UL 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* 19662306a36Sopenharmony_ci * A PPC64 ABIv2 function may have a local and a global entry 19762306a36Sopenharmony_ci * point. We need to use the local entry point when patching 19862306a36Sopenharmony_ci * functions, so identify and step over the global entry point 19962306a36Sopenharmony_ci * sequence. 20062306a36Sopenharmony_ci * 20162306a36Sopenharmony_ci * The global entry point sequence is always of the form: 20262306a36Sopenharmony_ci * 20362306a36Sopenharmony_ci * addis r2,r12,XXXX 20462306a36Sopenharmony_ci * addi r2,r2,XXXX 20562306a36Sopenharmony_ci * 20662306a36Sopenharmony_ci * A linker optimisation may convert the addis to lis: 20762306a36Sopenharmony_ci * 20862306a36Sopenharmony_ci * lis r2,XXXX 20962306a36Sopenharmony_ci * addi r2,r2,XXXX 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_ci { 21262306a36Sopenharmony_ci const u32 *insn = (const u32 *)(uintptr_t)addr; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if ((((*insn & OP_RT_RA_MASK) == ADDIS_R2_R12) || 21562306a36Sopenharmony_ci ((*insn & OP_RT_RA_MASK) == LIS_R2)) && 21662306a36Sopenharmony_ci ((*(insn + 1) & OP_RT_RA_MASK) == ADDI_R2_R2)) 21762306a36Sopenharmony_ci return (uintptr_t)(insn + 2) - start + base; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci#endif 22062306a36Sopenharmony_ci return (uintptr_t)addr - start + base; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cissize_t get_rel_offset(uintptr_t addr) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci size_t start, end, offset; 22662306a36Sopenharmony_ci char buf[256]; 22762306a36Sopenharmony_ci FILE *f; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci f = fopen("/proc/self/maps", "r"); 23062306a36Sopenharmony_ci if (!f) 23162306a36Sopenharmony_ci return -errno; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &offset) == 4) { 23462306a36Sopenharmony_ci if (addr >= start && addr < end) { 23562306a36Sopenharmony_ci fclose(f); 23662306a36Sopenharmony_ci return (size_t)addr - start + offset; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci fclose(f); 24162306a36Sopenharmony_ci return -EINVAL; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int 24562306a36Sopenharmony_ciparse_build_id_buf(const void *note_start, Elf32_Word note_size, char *build_id) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci Elf32_Word note_offs = 0; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci while (note_offs + sizeof(Elf32_Nhdr) < note_size) { 25062306a36Sopenharmony_ci Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (nhdr->n_type == 3 && nhdr->n_namesz == sizeof("GNU") && 25362306a36Sopenharmony_ci !strcmp((char *)(nhdr + 1), "GNU") && nhdr->n_descsz > 0 && 25462306a36Sopenharmony_ci nhdr->n_descsz <= BPF_BUILD_ID_SIZE) { 25562306a36Sopenharmony_ci memcpy(build_id, note_start + note_offs + 25662306a36Sopenharmony_ci ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr), nhdr->n_descsz); 25762306a36Sopenharmony_ci memset(build_id + nhdr->n_descsz, 0, BPF_BUILD_ID_SIZE - nhdr->n_descsz); 25862306a36Sopenharmony_ci return (int) nhdr->n_descsz; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci note_offs = note_offs + sizeof(Elf32_Nhdr) + 26262306a36Sopenharmony_ci ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return -ENOENT; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* Reads binary from *path* file and returns it in the *build_id* buffer 26962306a36Sopenharmony_ci * with *size* which is expected to be at least BPF_BUILD_ID_SIZE bytes. 27062306a36Sopenharmony_ci * Returns size of build id on success. On error the error value is 27162306a36Sopenharmony_ci * returned. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ciint read_build_id(const char *path, char *build_id, size_t size) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci int fd, err = -EINVAL; 27662306a36Sopenharmony_ci Elf *elf = NULL; 27762306a36Sopenharmony_ci GElf_Ehdr ehdr; 27862306a36Sopenharmony_ci size_t max, i; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (size < BPF_BUILD_ID_SIZE) 28162306a36Sopenharmony_ci return -EINVAL; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci fd = open(path, O_RDONLY | O_CLOEXEC); 28462306a36Sopenharmony_ci if (fd < 0) 28562306a36Sopenharmony_ci return -errno; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci (void)elf_version(EV_CURRENT); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); 29062306a36Sopenharmony_ci if (!elf) 29162306a36Sopenharmony_ci goto out; 29262306a36Sopenharmony_ci if (elf_kind(elf) != ELF_K_ELF) 29362306a36Sopenharmony_ci goto out; 29462306a36Sopenharmony_ci if (!gelf_getehdr(elf, &ehdr)) 29562306a36Sopenharmony_ci goto out; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci for (i = 0; i < ehdr.e_phnum; i++) { 29862306a36Sopenharmony_ci GElf_Phdr mem, *phdr; 29962306a36Sopenharmony_ci char *data; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci phdr = gelf_getphdr(elf, i, &mem); 30262306a36Sopenharmony_ci if (!phdr) 30362306a36Sopenharmony_ci goto out; 30462306a36Sopenharmony_ci if (phdr->p_type != PT_NOTE) 30562306a36Sopenharmony_ci continue; 30662306a36Sopenharmony_ci data = elf_rawfile(elf, &max); 30762306a36Sopenharmony_ci if (!data) 30862306a36Sopenharmony_ci goto out; 30962306a36Sopenharmony_ci if (phdr->p_offset + phdr->p_memsz > max) 31062306a36Sopenharmony_ci goto out; 31162306a36Sopenharmony_ci err = parse_build_id_buf(data + phdr->p_offset, phdr->p_memsz, build_id); 31262306a36Sopenharmony_ci if (err > 0) 31362306a36Sopenharmony_ci break; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ciout: 31762306a36Sopenharmony_ci if (elf) 31862306a36Sopenharmony_ci elf_end(elf); 31962306a36Sopenharmony_ci close(fd); 32062306a36Sopenharmony_ci return err; 32162306a36Sopenharmony_ci} 322