247 lines · c
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_func32 * prev_i: Index to the last found ksym_func33 */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 for113 * 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