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 11 struct 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 22 static 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 84 errout: 85 return -EINVAL; 86 } 87 88 void 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 100 struct 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 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 NULL; 118 119 prog_linfo = calloc(1, sizeof(*prog_linfo)); 120 if (!prog_linfo) 121 return 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 175 err_free: 176 bpf_prog_linfo__free(prog_linfo); 177 return NULL; 178 } 179 180 const struct bpf_line_info * 181 bpf_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 NULL; 190 191 nr_linfo = prog_linfo->nr_jited_linfo_per_func[func_idx]; 192 if (nr_skip >= nr_linfo) 193 return 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 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 218 const struct bpf_line_info * 219 bpf_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 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 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