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 += roundup(count * size, sizeof(__u64)); 153 } 154 155 /* step 3: allocate continuous memory */ 156 info_linear = malloc(sizeof(struct perf_bpil) + data_len); 157 if (!info_linear) 158 return ERR_PTR(-ENOMEM); 159 160 /* step 4: fill data to info_linear->info */ 161 info_linear->arrays = arrays; 162 memset(&info_linear->info, 0, sizeof(info)); 163 ptr = info_linear->data; 164 165 for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { 166 struct bpil_array_desc *desc; 167 __u32 count, size; 168 169 if ((arrays & (1UL << i)) == 0) 170 continue; 171 172 desc = bpil_array_desc + i; 173 count = bpf_prog_info_read_offset_u32(&info, desc->count_offset); 174 size = bpf_prog_info_read_offset_u32(&info, desc->size_offset); 175 bpf_prog_info_set_offset_u32(&info_linear->info, 176 desc->count_offset, count); 177 bpf_prog_info_set_offset_u32(&info_linear->info, 178 desc->size_offset, size); 179 bpf_prog_info_set_offset_u64(&info_linear->info, 180 desc->array_offset, 181 ptr_to_u64(ptr)); 182 ptr += roundup(count * size, sizeof(__u64)); 183 } 184 185 /* step 5: call syscall again to get required arrays */ 186 err = bpf_obj_get_info_by_fd(fd, &info_linear->info, &info_len); 187 if (err) { 188 pr_debug("can't get prog info: %s", strerror(errno)); 189 free(info_linear); 190 return ERR_PTR(-EFAULT); 191 } 192 193 /* step 6: verify the data */ 194 for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { 195 struct bpil_array_desc *desc; 196 __u32 v1, v2; 197 198 if ((arrays & (1UL << i)) == 0) 199 continue; 200 201 desc = bpil_array_desc + i; 202 v1 = bpf_prog_info_read_offset_u32(&info, desc->count_offset); 203 v2 = bpf_prog_info_read_offset_u32(&info_linear->info, 204 desc->count_offset); 205 if (v1 != v2) 206 pr_warning("%s: mismatch in element count\n", __func__); 207 208 v1 = bpf_prog_info_read_offset_u32(&info, desc->size_offset); 209 v2 = bpf_prog_info_read_offset_u32(&info_linear->info, 210 desc->size_offset); 211 if (v1 != v2) 212 pr_warning("%s: mismatch in rec size\n", __func__); 213 } 214 215 /* step 7: update info_len and data_len */ 216 info_linear->info_len = sizeof(struct bpf_prog_info); 217 info_linear->data_len = data_len; 218 219 return info_linear; 220 } 221 222 void bpil_addr_to_offs(struct perf_bpil *info_linear) 223 { 224 int i; 225 226 for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { 227 struct bpil_array_desc *desc; 228 __u64 addr, offs; 229 230 if ((info_linear->arrays & (1UL << i)) == 0) 231 continue; 232 233 desc = bpil_array_desc + i; 234 addr = bpf_prog_info_read_offset_u64(&info_linear->info, 235 desc->array_offset); 236 offs = addr - ptr_to_u64(info_linear->data); 237 bpf_prog_info_set_offset_u64(&info_linear->info, 238 desc->array_offset, offs); 239 } 240 } 241 242 void bpil_offs_to_addr(struct perf_bpil *info_linear) 243 { 244 int i; 245 246 for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { 247 struct bpil_array_desc *desc; 248 __u64 addr, offs; 249 250 if ((info_linear->arrays & (1UL << i)) == 0) 251 continue; 252 253 desc = bpil_array_desc + i; 254 offs = bpf_prog_info_read_offset_u64(&info_linear->info, 255 desc->array_offset); 256 addr = offs + ptr_to_u64(info_linear->data); 257 bpf_prog_info_set_offset_u64(&info_linear->info, 258 desc->array_offset, addr); 259 } 260 } 261