Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <ctype.h>
0003 #include <stdio.h>
0004 #include <stdlib.h>
0005 #include <string.h>
0006 #include <assert.h>
0007 #include <errno.h>
0008 #include <fcntl.h>
0009 #include <poll.h>
0010 #include <unistd.h>
0011 #include <linux/perf_event.h>
0012 #include <sys/mman.h>
0013 #include "trace_helpers.h"
0014 
0015 #define DEBUGFS "/sys/kernel/debug/tracing/"
0016 
0017 #define MAX_SYMS 300000
0018 static struct ksym syms[MAX_SYMS];
0019 static int sym_cnt;
0020 
0021 static int ksym_cmp(const void *p1, const void *p2)
0022 {
0023     return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr;
0024 }
0025 
0026 int load_kallsyms(void)
0027 {
0028     FILE *f;
0029     char func[256], buf[256];
0030     char symbol;
0031     void *addr;
0032     int i = 0;
0033 
0034     /*
0035      * This is called/used from multiplace places,
0036      * load symbols just once.
0037      */
0038     if (sym_cnt)
0039         return 0;
0040 
0041     f = fopen("/proc/kallsyms", "r");
0042     if (!f)
0043         return -ENOENT;
0044 
0045     while (fgets(buf, sizeof(buf), f)) {
0046         if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3)
0047             break;
0048         if (!addr)
0049             continue;
0050         syms[i].addr = (long) addr;
0051         syms[i].name = strdup(func);
0052         i++;
0053     }
0054     fclose(f);
0055     sym_cnt = i;
0056     qsort(syms, sym_cnt, sizeof(struct ksym), ksym_cmp);
0057     return 0;
0058 }
0059 
0060 struct ksym *ksym_search(long key)
0061 {
0062     int start = 0, end = sym_cnt;
0063     int result;
0064 
0065     /* kallsyms not loaded. return NULL */
0066     if (sym_cnt <= 0)
0067         return NULL;
0068 
0069     while (start < end) {
0070         size_t mid = start + (end - start) / 2;
0071 
0072         result = key - syms[mid].addr;
0073         if (result < 0)
0074             end = mid;
0075         else if (result > 0)
0076             start = mid + 1;
0077         else
0078             return &syms[mid];
0079     }
0080 
0081     if (start >= 1 && syms[start - 1].addr < key &&
0082         key < syms[start].addr)
0083         /* valid ksym */
0084         return &syms[start - 1];
0085 
0086     /* out of range. return _stext */
0087     return &syms[0];
0088 }
0089 
0090 long ksym_get_addr(const char *name)
0091 {
0092     int i;
0093 
0094     for (i = 0; i < sym_cnt; i++) {
0095         if (strcmp(syms[i].name, name) == 0)
0096             return syms[i].addr;
0097     }
0098 
0099     return 0;
0100 }
0101 
0102 /* open kallsyms and read symbol addresses on the fly. Without caching all symbols,
0103  * this is faster than load + find.
0104  */
0105 int kallsyms_find(const char *sym, unsigned long long *addr)
0106 {
0107     char type, name[500];
0108     unsigned long long value;
0109     int err = 0;
0110     FILE *f;
0111 
0112     f = fopen("/proc/kallsyms", "r");
0113     if (!f)
0114         return -EINVAL;
0115 
0116     while (fscanf(f, "%llx %c %499s%*[^\n]\n", &value, &type, name) > 0) {
0117         if (strcmp(name, sym) == 0) {
0118             *addr = value;
0119             goto out;
0120         }
0121     }
0122     err = -ENOENT;
0123 
0124 out:
0125     fclose(f);
0126     return err;
0127 }
0128 
0129 void read_trace_pipe(void)
0130 {
0131     int trace_fd;
0132 
0133     trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0);
0134     if (trace_fd < 0)
0135         return;
0136 
0137     while (1) {
0138         static char buf[4096];
0139         ssize_t sz;
0140 
0141         sz = read(trace_fd, buf, sizeof(buf) - 1);
0142         if (sz > 0) {
0143             buf[sz] = 0;
0144             puts(buf);
0145         }
0146     }
0147 }
0148 
0149 ssize_t get_uprobe_offset(const void *addr)
0150 {
0151     size_t start, end, base;
0152     char buf[256];
0153     bool found = false;
0154     FILE *f;
0155 
0156     f = fopen("/proc/self/maps", "r");
0157     if (!f)
0158         return -errno;
0159 
0160     while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) == 4) {
0161         if (buf[2] == 'x' && (uintptr_t)addr >= start && (uintptr_t)addr < end) {
0162             found = true;
0163             break;
0164         }
0165     }
0166 
0167     fclose(f);
0168 
0169     if (!found)
0170         return -ESRCH;
0171 
0172 #if defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2
0173 
0174 #define OP_RT_RA_MASK   0xffff0000UL
0175 #define LIS_R2          0x3c400000UL
0176 #define ADDIS_R2_R12    0x3c4c0000UL
0177 #define ADDI_R2_R2      0x38420000UL
0178 
0179     /*
0180      * A PPC64 ABIv2 function may have a local and a global entry
0181      * point. We need to use the local entry point when patching
0182      * functions, so identify and step over the global entry point
0183      * sequence.
0184      *
0185      * The global entry point sequence is always of the form:
0186      *
0187      * addis r2,r12,XXXX
0188      * addi  r2,r2,XXXX
0189      *
0190      * A linker optimisation may convert the addis to lis:
0191      *
0192      * lis   r2,XXXX
0193      * addi  r2,r2,XXXX
0194      */
0195     {
0196         const u32 *insn = (const u32 *)(uintptr_t)addr;
0197 
0198         if ((((*insn & OP_RT_RA_MASK) == ADDIS_R2_R12) ||
0199              ((*insn & OP_RT_RA_MASK) == LIS_R2)) &&
0200             ((*(insn + 1) & OP_RT_RA_MASK) == ADDI_R2_R2))
0201             return (uintptr_t)(insn + 2) - start + base;
0202     }
0203 #endif
0204     return (uintptr_t)addr - start + base;
0205 }
0206 
0207 ssize_t get_rel_offset(uintptr_t addr)
0208 {
0209     size_t start, end, offset;
0210     char buf[256];
0211     FILE *f;
0212 
0213     f = fopen("/proc/self/maps", "r");
0214     if (!f)
0215         return -errno;
0216 
0217     while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &offset) == 4) {
0218         if (addr >= start && addr < end) {
0219             fclose(f);
0220             return (size_t)addr - start + offset;
0221         }
0222     }
0223 
0224     fclose(f);
0225     return -EINVAL;
0226 }