1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 3 #ifndef _GNU_SOURCE 4 #define _GNU_SOURCE 5 #endif 6 7 #include <errno.h> 8 #include <stdlib.h> 9 #include <linux/err.h> 10 #include <linux/kernel.h> 11 #include <bpf/bpf.h> 12 #include "bpf-utils.h" 13 #include "debug.h" 14 15 struct bpil_array_desc { 16 int array_offset; /* e.g. offset of jited_prog_insns */ 17 int count_offset; /* e.g. offset of jited_prog_len */ 18 int size_offset; /* > 0: offset of rec size, 19 * < 0: fix size of -size_offset 20 */ 21 }; 22 23 static struct bpil_array_desc bpil_array_desc[] = { 24 [PERF_BPIL_JITED_INSNS] = { 25 offsetof(struct bpf_prog_info, jited_prog_insns), 26 offsetof(struct bpf_prog_info, jited_prog_len), 27 -1, 28 }, 29 [PERF_BPIL_XLATED_INSNS] = { 30 offsetof(struct bpf_prog_info, xlated_prog_insns), 31 offsetof(struct bpf_prog_info, xlated_prog_len), 32 -1, 33 }, 34 [PERF_BPIL_MAP_IDS] = { 35 offsetof(struct bpf_prog_info, map_ids), 36 offsetof(struct bpf_prog_info, nr_map_ids), 37 -(int)sizeof(__u32), 38 }, 39 [PERF_BPIL_JITED_KSYMS] = { 40 offsetof(struct bpf_prog_info, jited_ksyms), 41 offsetof(struct bpf_prog_info, nr_jited_ksyms), 42 -(int)sizeof(__u64), 43 }, 44 [PERF_BPIL_JITED_FUNC_LENS] = { 45 offsetof(struct bpf_prog_info, jited_func_lens), 46 offsetof(struct bpf_prog_info, nr_jited_func_lens), 47 -(int)sizeof(__u32), 48 }, 49 [PERF_BPIL_FUNC_INFO] = { 50 offsetof(struct bpf_prog_info, func_info), 51 offsetof(struct bpf_prog_info, nr_func_info), 52 offsetof(struct bpf_prog_info, func_info_rec_size), 53 }, 54 [PERF_BPIL_LINE_INFO] = { 55 offsetof(struct bpf_prog_info, line_info), 56 offsetof(struct bpf_prog_info, nr_line_info), 57 offsetof(struct bpf_prog_info, line_info_rec_size), 58 }, 59 [PERF_BPIL_JITED_LINE_INFO] = { 60 offsetof(struct bpf_prog_info, jited_line_info), 61 offsetof(struct bpf_prog_info, nr_jited_line_info), 62 offsetof(struct bpf_prog_info, jited_line_info_rec_size), 63 }, 64 [PERF_BPIL_PROG_TAGS] = { 65 offsetof(struct bpf_prog_info, prog_tags), 66 offsetof(struct bpf_prog_info, nr_prog_tags), 67 -(int)sizeof(__u8) * BPF_TAG_SIZE, 68 }, 69 70 }; 71 72 static __u32 bpf_prog_info_read_offset_u32(struct bpf_prog_info *info, 73 int offset) 74 { 75 __u32 *array = (__u32 *)info; 76 77 if (offset >= 0) 78 return array[offset / sizeof(__u32)]; 79 return -(int)offset; 80 } 81 82 static __u64 bpf_prog_info_read_offset_u64(struct bpf_prog_info *info, 83 int offset) 84 { 85 __u64 *array = (__u64 *)info; 86 87 if (offset >= 0) 88 return array[offset / sizeof(__u64)]; 89 return -(int)offset; 90 } 91 92 static void bpf_prog_info_set_offset_u32(struct bpf_prog_info *info, int offset, 93 __u32 val) 94 { 95 __u32 *array = (__u32 *)info; 96 97 if (offset >= 0) 98 array[offset / sizeof(__u32)] = val; 99 } 100 101 static void bpf_prog_info_set_offset_u64(struct bpf_prog_info *info, int offset, 102 __u64 val) 103 { 104 __u64 *array = (__u64 *)info; 105 106 if (offset >= 0) 107 array[offset / sizeof(__u64)] = val; 108 } 109 110 struct perf_bpil * 111 get_bpf_prog_info_linear(int fd, __u64 arrays) 112 { 113 struct bpf_prog_info info = {}; 114 struct perf_bpil *info_linear; 115 __u32 info_len = sizeof(info); 116 __u32 data_len = 0; 117 int i, err; 118 void *ptr; 119 120 if (arrays >> PERF_BPIL_LAST_ARRAY) 121 return ERR_PTR(-EINVAL); 122 123 /* step 1: get array dimensions */ 124 err = bpf_obj_get_info_by_fd(fd, &info, &info_len); 125 if (err) { 126 pr_debug("can't get prog info: %s", strerror(errno)); 127 return ERR_PTR(-EFAULT); 128 } 129 130 /* step 2: calculate total size of all arrays */ 131 for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { 132 bool include_array = (arrays & (1UL << i)) > 0; 133 struct bpil_array_desc *desc; 134 __u32 count, size; 135 136 desc = bpil_array_desc + i; 137 138 /* kernel is too old to support this field */ 139 if (info_len < desc->array_offset + sizeof(__u32) || 140 info_len < desc->count_offset + sizeof(__u32) || 141 (desc->size_offset > 0 && info_len < (__u32)desc->size_offset)) 142 include_array = false; 143 144 if (!include_array) { 145 arrays &= ~(1UL << i); /* clear the bit */ 146 continue; 147 } 148 149 count = bpf_prog_info_read_offset_u32(&info, desc->count_offset); 150 size = bpf_prog_info_read_offset_u32(&info, desc->size_offset); 151 152 data_len += count * size; 153 } 154 155 /* step 3: allocate continuous memory */ 156 data_len = roundup(data_len, sizeof(__u64)); 157 info_linear = malloc(sizeof(struct perf_bpil) + data_len); 158 if (!info_linear) 159 return ERR_PTR(-ENOMEM); 160 161 /* step 4: fill data to info_linear->info */ 162 info_linear->arrays = arrays; 163 memset(&info_linear->info, 0, sizeof(info)); 164 ptr = info_linear->data; 165 166 for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { 167 struct bpil_array_desc *desc; 168 __u32 count, size; 169 170 if ((arrays & (1UL << i)) == 0) 171 continue; 172 173 desc = bpil_array_desc + i; 174 count = bpf_prog_info_read_offset_u32(&info, desc->count_offset); 175 size = bpf_prog_info_read_offset_u32(&info, desc->size_offset); 176 bpf_prog_info_set_offset_u32(&info_linear->info, 177 desc->count_offset, count); 178 bpf_prog_info_set_offset_u32(&info_linear->info, 179 desc->size_offset, size); 180 bpf_prog_info_set_offset_u64(&info_linear->info, 181 desc->array_offset, 182 ptr_to_u64(ptr)); 183 ptr += count * size; 184 } 185 186 /* step 5: call syscall again to get required arrays */ 187 err = bpf_obj_get_info_by_fd(fd, &info_linear->info, &info_len); 188 if (err) { 189 pr_debug("can't get prog info: %s", strerror(errno)); 190 free(info_linear); 191 return ERR_PTR(-EFAULT); 192 } 193 194 /* step 6: verify the data */ 195 for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { 196 struct bpil_array_desc *desc; 197 __u32 v1, v2; 198 199 if ((arrays & (1UL << i)) == 0) 200 continue; 201 202 desc = bpil_array_desc + i; 203 v1 = bpf_prog_info_read_offset_u32(&info, desc->count_offset); 204 v2 = bpf_prog_info_read_offset_u32(&info_linear->info, 205 desc->count_offset); 206 if (v1 != v2) 207 pr_warning("%s: mismatch in element count\n", __func__); 208 209 v1 = bpf_prog_info_read_offset_u32(&info, desc->size_offset); 210 v2 = bpf_prog_info_read_offset_u32(&info_linear->info, 211 desc->size_offset); 212 if (v1 != v2) 213 pr_warning("%s: mismatch in rec size\n", __func__); 214 } 215 216 /* step 7: update info_len and data_len */ 217 info_linear->info_len = sizeof(struct bpf_prog_info); 218 info_linear->data_len = data_len; 219 220 return info_linear; 221 } 222 223 void bpil_addr_to_offs(struct perf_bpil *info_linear) 224 { 225 int i; 226 227 for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { 228 struct bpil_array_desc *desc; 229 __u64 addr, offs; 230 231 if ((info_linear->arrays & (1UL << i)) == 0) 232 continue; 233 234 desc = bpil_array_desc + i; 235 addr = bpf_prog_info_read_offset_u64(&info_linear->info, 236 desc->array_offset); 237 offs = addr - ptr_to_u64(info_linear->data); 238 bpf_prog_info_set_offset_u64(&info_linear->info, 239 desc->array_offset, offs); 240 } 241 } 242 243 void bpil_offs_to_addr(struct perf_bpil *info_linear) 244 { 245 int i; 246 247 for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { 248 struct bpil_array_desc *desc; 249 __u64 addr, offs; 250 251 if ((info_linear->arrays & (1UL << i)) == 0) 252 continue; 253 254 desc = bpil_array_desc + i; 255 offs = bpf_prog_info_read_offset_u64(&info_linear->info, 256 desc->array_offset); 257 addr = offs + ptr_to_u64(info_linear->data); 258 bpf_prog_info_set_offset_u64(&info_linear->info, 259 desc->array_offset, addr); 260 } 261 } 262