linux/tools/lib/bpf/bpf_prog_linfo.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
   2/* Copyright (c) 2018 Facebook */
   3
   4#include <string.h>
   5#include <stdlib.h>
   6#include <linux/err.h>
   7#include <linux/bpf.h>
   8#include "libbpf.h"
   9#include "libbpf_internal.h"
  10
  11struct bpf_prog_linfo {
  12        void *raw_linfo;
  13        void *raw_jited_linfo;
  14        __u32 *nr_jited_linfo_per_func;
  15        __u32 *jited_linfo_func_idx;
  16        __u32 nr_linfo;
  17        __u32 nr_jited_func;
  18        __u32 rec_size;
  19        __u32 jited_rec_size;
  20};
  21
  22static int dissect_jited_func(struct bpf_prog_linfo *prog_linfo,
  23                              const __u64 *ksym_func, const __u32 *ksym_len)
  24{
  25        __u32 nr_jited_func, nr_linfo;
  26        const void *raw_jited_linfo;
  27        const __u64 *jited_linfo;
  28        __u64 last_jited_linfo;
  29        /*
  30         * Index to raw_jited_linfo:
  31         *      i: Index for searching the next ksym_func
  32         * prev_i: Index to the last found ksym_func
  33         */
  34        __u32 i, prev_i;
  35        __u32 f; /* Index to ksym_func */
  36
  37        raw_jited_linfo = prog_linfo->raw_jited_linfo;
  38        jited_linfo = raw_jited_linfo;
  39        if (ksym_func[0] != *jited_linfo)
  40                goto errout;
  41
  42        prog_linfo->jited_linfo_func_idx[0] = 0;
  43        nr_jited_func = prog_linfo->nr_jited_func;
  44        nr_linfo = prog_linfo->nr_linfo;
  45
  46        for (prev_i = 0, i = 1, f = 1;
  47             i < nr_linfo && f < nr_jited_func;
  48             i++) {
  49                raw_jited_linfo += prog_linfo->jited_rec_size;
  50                last_jited_linfo = *jited_linfo;
  51                jited_linfo = raw_jited_linfo;
  52
  53                if (ksym_func[f] == *jited_linfo) {
  54                        prog_linfo->jited_linfo_func_idx[f] = i;
  55
  56                        /* Sanity check */
  57                        if (last_jited_linfo - ksym_func[f - 1] + 1 >
  58                            ksym_len[f - 1])
  59                                goto errout;
  60
  61                        prog_linfo->nr_jited_linfo_per_func[f - 1] =
  62                                i - prev_i;
  63                        prev_i = i;
  64
  65                        /*
  66                         * The ksym_func[f] is found in jited_linfo.
  67                         * Look for the next one.
  68                         */
  69                        f++;
  70                } else if (*jited_linfo <= last_jited_linfo) {
  71                        /* Ensure the addr is increasing _within_ a func */
  72                        goto errout;
  73                }
  74        }
  75
  76        if (f != nr_jited_func)
  77                goto errout;
  78
  79        prog_linfo->nr_jited_linfo_per_func[nr_jited_func - 1] =
  80                nr_linfo - prev_i;
  81
  82        return 0;
  83
  84errout:
  85        return -EINVAL;
  86}
  87
  88void bpf_prog_linfo__free(struct bpf_prog_linfo *prog_linfo)
  89{
  90        if (!prog_linfo)
  91                return;
  92
  93        free(prog_linfo->raw_linfo);
  94        free(prog_linfo->raw_jited_linfo);
  95        free(prog_linfo->nr_jited_linfo_per_func);
  96        free(prog_linfo->jited_linfo_func_idx);
  97        free(prog_linfo);
  98}
  99
 100struct bpf_prog_linfo *bpf_prog_linfo__new(const struct bpf_prog_info *info)
 101{
 102        struct bpf_prog_linfo *prog_linfo;
 103        __u32 nr_linfo, nr_jited_func;
 104        __u64 data_sz;
 105
 106        nr_linfo = info->nr_line_info;
 107
 108        if (!nr_linfo)
 109                return errno = EINVAL, NULL;
 110
 111        /*
 112         * The min size that bpf_prog_linfo has to access for
 113         * searching purpose.
 114         */
 115        if (info->line_info_rec_size <
 116            offsetof(struct bpf_line_info, file_name_off))
 117                return errno = EINVAL, NULL;
 118
 119        prog_linfo = calloc(1, sizeof(*prog_linfo));
 120        if (!prog_linfo)
 121                return errno = ENOMEM, NULL;
 122
 123        /* Copy xlated line_info */
 124        prog_linfo->nr_linfo = nr_linfo;
 125        prog_linfo->rec_size = info->line_info_rec_size;
 126        data_sz = (__u64)nr_linfo * prog_linfo->rec_size;
 127        prog_linfo->raw_linfo = malloc(data_sz);
 128        if (!prog_linfo->raw_linfo)
 129                goto err_free;
 130        memcpy(prog_linfo->raw_linfo, (void *)(long)info->line_info, data_sz);
 131
 132        nr_jited_func = info->nr_jited_ksyms;
 133        if (!nr_jited_func ||
 134            !info->jited_line_info ||
 135            info->nr_jited_line_info != nr_linfo ||
 136            info->jited_line_info_rec_size < sizeof(__u64) ||
 137            info->nr_jited_func_lens != nr_jited_func ||
 138            !info->jited_ksyms ||
 139            !info->jited_func_lens)
 140                /* Not enough info to provide jited_line_info */
 141                return prog_linfo;
 142
 143        /* Copy jited_line_info */
 144        prog_linfo->nr_jited_func = nr_jited_func;
 145        prog_linfo->jited_rec_size = info->jited_line_info_rec_size;
 146        data_sz = (__u64)nr_linfo * prog_linfo->jited_rec_size;
 147        prog_linfo->raw_jited_linfo = malloc(data_sz);
 148        if (!prog_linfo->raw_jited_linfo)
 149                goto err_free;
 150        memcpy(prog_linfo->raw_jited_linfo,
 151               (void *)(long)info->jited_line_info, data_sz);
 152
 153        /* Number of jited_line_info per jited func */
 154        prog_linfo->nr_jited_linfo_per_func = malloc(nr_jited_func *
 155                                                     sizeof(__u32));
 156        if (!prog_linfo->nr_jited_linfo_per_func)
 157                goto err_free;
 158
 159        /*
 160         * For each jited func,
 161         * the start idx to the "linfo" and "jited_linfo" array,
 162         */
 163        prog_linfo->jited_linfo_func_idx = malloc(nr_jited_func *
 164                                                  sizeof(__u32));
 165        if (!prog_linfo->jited_linfo_func_idx)
 166                goto err_free;
 167
 168        if (dissect_jited_func(prog_linfo,
 169                               (__u64 *)(long)info->jited_ksyms,
 170                               (__u32 *)(long)info->jited_func_lens))
 171                goto err_free;
 172
 173        return prog_linfo;
 174
 175err_free:
 176        bpf_prog_linfo__free(prog_linfo);
 177        return errno = EINVAL, NULL;
 178}
 179
 180const struct bpf_line_info *
 181bpf_prog_linfo__lfind_addr_func(const struct bpf_prog_linfo *prog_linfo,
 182                                __u64 addr, __u32 func_idx, __u32 nr_skip)
 183{
 184        __u32 jited_rec_size, rec_size, nr_linfo, start, i;
 185        const void *raw_jited_linfo, *raw_linfo;
 186        const __u64 *jited_linfo;
 187
 188        if (func_idx >= prog_linfo->nr_jited_func)
 189                return errno = ENOENT, NULL;
 190
 191        nr_linfo = prog_linfo->nr_jited_linfo_per_func[func_idx];
 192        if (nr_skip >= nr_linfo)
 193                return errno = ENOENT, NULL;
 194
 195        start = prog_linfo->jited_linfo_func_idx[func_idx] + nr_skip;
 196        jited_rec_size = prog_linfo->jited_rec_size;
 197        raw_jited_linfo = prog_linfo->raw_jited_linfo +
 198                (start * jited_rec_size);
 199        jited_linfo = raw_jited_linfo;
 200        if (addr < *jited_linfo)
 201                return errno = ENOENT, NULL;
 202
 203        nr_linfo -= nr_skip;
 204        rec_size = prog_linfo->rec_size;
 205        raw_linfo = prog_linfo->raw_linfo + (start * rec_size);
 206        for (i = 0; i < nr_linfo; i++) {
 207                if (addr < *jited_linfo)
 208                        break;
 209
 210                raw_linfo += rec_size;
 211                raw_jited_linfo += jited_rec_size;
 212                jited_linfo = raw_jited_linfo;
 213        }
 214
 215        return raw_linfo - rec_size;
 216}
 217
 218const struct bpf_line_info *
 219bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
 220                      __u32 insn_off, __u32 nr_skip)
 221{
 222        const struct bpf_line_info *linfo;
 223        __u32 rec_size, nr_linfo, i;
 224        const void *raw_linfo;
 225
 226        nr_linfo = prog_linfo->nr_linfo;
 227        if (nr_skip >= nr_linfo)
 228                return errno = ENOENT, NULL;
 229
 230        rec_size = prog_linfo->rec_size;
 231        raw_linfo = prog_linfo->raw_linfo + (nr_skip * rec_size);
 232        linfo = raw_linfo;
 233        if (insn_off < linfo->insn_off)
 234                return errno = ENOENT, NULL;
 235
 236        nr_linfo -= nr_skip;
 237        for (i = 0; i < nr_linfo; i++) {
 238                if (insn_off < linfo->insn_off)
 239                        break;
 240
 241                raw_linfo += rec_size;
 242                linfo = raw_linfo;
 243        }
 244
 245        return raw_linfo - rec_size;
 246}
 247