Commit | Line | Data |
---|---|---|
1b76c13e WN |
1 | /* |
2 | * Common eBPF ELF object loading operations. | |
3 | * | |
4 | * Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org> | |
5 | * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com> | |
6 | * Copyright (C) 2015 Huawei Inc. | |
7 | */ | |
8 | ||
9 | #include <stdlib.h> | |
b3f59d66 WN |
10 | #include <stdio.h> |
11 | #include <stdarg.h> | |
12 | #include <string.h> | |
1b76c13e | 13 | #include <unistd.h> |
1a5e3fb1 WN |
14 | #include <fcntl.h> |
15 | #include <errno.h> | |
1b76c13e | 16 | #include <asm/unistd.h> |
cb1e5e96 | 17 | #include <linux/kernel.h> |
1b76c13e | 18 | #include <linux/bpf.h> |
1a5e3fb1 WN |
19 | #include <libelf.h> |
20 | #include <gelf.h> | |
1b76c13e WN |
21 | |
22 | #include "libbpf.h" | |
b3f59d66 WN |
23 | |
24 | #define __printf(a, b) __attribute__((format(printf, a, b))) | |
25 | ||
26 | __printf(1, 2) | |
27 | static int __base_pr(const char *format, ...) | |
28 | { | |
29 | va_list args; | |
30 | int err; | |
31 | ||
32 | va_start(args, format); | |
33 | err = vfprintf(stderr, format, args); | |
34 | va_end(args); | |
35 | return err; | |
36 | } | |
37 | ||
38 | static __printf(1, 2) libbpf_print_fn_t __pr_warning = __base_pr; | |
39 | static __printf(1, 2) libbpf_print_fn_t __pr_info = __base_pr; | |
40 | static __printf(1, 2) libbpf_print_fn_t __pr_debug; | |
41 | ||
42 | #define __pr(func, fmt, ...) \ | |
43 | do { \ | |
44 | if ((func)) \ | |
45 | (func)("libbpf: " fmt, ##__VA_ARGS__); \ | |
46 | } while (0) | |
47 | ||
48 | #define pr_warning(fmt, ...) __pr(__pr_warning, fmt, ##__VA_ARGS__) | |
49 | #define pr_info(fmt, ...) __pr(__pr_info, fmt, ##__VA_ARGS__) | |
50 | #define pr_debug(fmt, ...) __pr(__pr_debug, fmt, ##__VA_ARGS__) | |
51 | ||
52 | void libbpf_set_print(libbpf_print_fn_t warn, | |
53 | libbpf_print_fn_t info, | |
54 | libbpf_print_fn_t debug) | |
55 | { | |
56 | __pr_warning = warn; | |
57 | __pr_info = info; | |
58 | __pr_debug = debug; | |
59 | } | |
1a5e3fb1 WN |
60 | |
61 | /* Copied from tools/perf/util/util.h */ | |
62 | #ifndef zfree | |
63 | # define zfree(ptr) ({ free(*ptr); *ptr = NULL; }) | |
64 | #endif | |
65 | ||
66 | #ifndef zclose | |
67 | # define zclose(fd) ({ \ | |
68 | int ___err = 0; \ | |
69 | if ((fd) >= 0) \ | |
70 | ___err = close((fd)); \ | |
71 | fd = -1; \ | |
72 | ___err; }) | |
73 | #endif | |
74 | ||
75 | #ifdef HAVE_LIBELF_MMAP_SUPPORT | |
76 | # define LIBBPF_ELF_C_READ_MMAP ELF_C_READ_MMAP | |
77 | #else | |
78 | # define LIBBPF_ELF_C_READ_MMAP ELF_C_READ | |
79 | #endif | |
80 | ||
a5b8bd47 WN |
81 | /* |
82 | * bpf_prog should be a better name but it has been used in | |
83 | * linux/filter.h. | |
84 | */ | |
85 | struct bpf_program { | |
86 | /* Index in elf obj file, for relocation use. */ | |
87 | int idx; | |
88 | char *section_name; | |
89 | struct bpf_insn *insns; | |
90 | size_t insns_cnt; | |
91 | }; | |
92 | ||
1a5e3fb1 | 93 | struct bpf_object { |
cb1e5e96 WN |
94 | char license[64]; |
95 | u32 kern_version; | |
0b3d1efa WN |
96 | void *maps_buf; |
97 | size_t maps_buf_sz; | |
98 | ||
a5b8bd47 WN |
99 | struct bpf_program *programs; |
100 | size_t nr_programs; | |
101 | ||
1a5e3fb1 WN |
102 | /* |
103 | * Information when doing elf related work. Only valid if fd | |
104 | * is valid. | |
105 | */ | |
106 | struct { | |
107 | int fd; | |
6c956392 WN |
108 | void *obj_buf; |
109 | size_t obj_buf_sz; | |
1a5e3fb1 WN |
110 | Elf *elf; |
111 | GElf_Ehdr ehdr; | |
bec7d68c | 112 | Elf_Data *symbols; |
b62f06e8 WN |
113 | struct { |
114 | GElf_Shdr shdr; | |
115 | Elf_Data *data; | |
116 | } *reloc; | |
117 | int nr_reloc; | |
1a5e3fb1 WN |
118 | } efile; |
119 | char path[]; | |
120 | }; | |
121 | #define obj_elf_valid(o) ((o)->efile.elf) | |
122 | ||
a5b8bd47 WN |
123 | static void bpf_program__exit(struct bpf_program *prog) |
124 | { | |
125 | if (!prog) | |
126 | return; | |
127 | ||
128 | zfree(&prog->section_name); | |
129 | zfree(&prog->insns); | |
130 | prog->insns_cnt = 0; | |
131 | prog->idx = -1; | |
132 | } | |
133 | ||
134 | static int | |
135 | bpf_program__init(void *data, size_t size, char *name, int idx, | |
136 | struct bpf_program *prog) | |
137 | { | |
138 | if (size < sizeof(struct bpf_insn)) { | |
139 | pr_warning("corrupted section '%s'\n", name); | |
140 | return -EINVAL; | |
141 | } | |
142 | ||
143 | bzero(prog, sizeof(*prog)); | |
144 | ||
145 | prog->section_name = strdup(name); | |
146 | if (!prog->section_name) { | |
147 | pr_warning("failed to alloc name for prog %s\n", | |
148 | name); | |
149 | goto errout; | |
150 | } | |
151 | ||
152 | prog->insns = malloc(size); | |
153 | if (!prog->insns) { | |
154 | pr_warning("failed to alloc insns for %s\n", name); | |
155 | goto errout; | |
156 | } | |
157 | prog->insns_cnt = size / sizeof(struct bpf_insn); | |
158 | memcpy(prog->insns, data, | |
159 | prog->insns_cnt * sizeof(struct bpf_insn)); | |
160 | prog->idx = idx; | |
161 | ||
162 | return 0; | |
163 | errout: | |
164 | bpf_program__exit(prog); | |
165 | return -ENOMEM; | |
166 | } | |
167 | ||
168 | static int | |
169 | bpf_object__add_program(struct bpf_object *obj, void *data, size_t size, | |
170 | char *name, int idx) | |
171 | { | |
172 | struct bpf_program prog, *progs; | |
173 | int nr_progs, err; | |
174 | ||
175 | err = bpf_program__init(data, size, name, idx, &prog); | |
176 | if (err) | |
177 | return err; | |
178 | ||
179 | progs = obj->programs; | |
180 | nr_progs = obj->nr_programs; | |
181 | ||
182 | progs = realloc(progs, sizeof(progs[0]) * (nr_progs + 1)); | |
183 | if (!progs) { | |
184 | /* | |
185 | * In this case the original obj->programs | |
186 | * is still valid, so don't need special treat for | |
187 | * bpf_close_object(). | |
188 | */ | |
189 | pr_warning("failed to alloc a new program '%s'\n", | |
190 | name); | |
191 | bpf_program__exit(&prog); | |
192 | return -ENOMEM; | |
193 | } | |
194 | ||
195 | pr_debug("found program %s\n", prog.section_name); | |
196 | obj->programs = progs; | |
197 | obj->nr_programs = nr_progs + 1; | |
198 | progs[nr_progs] = prog; | |
199 | return 0; | |
200 | } | |
201 | ||
6c956392 WN |
202 | static struct bpf_object *bpf_object__new(const char *path, |
203 | void *obj_buf, | |
204 | size_t obj_buf_sz) | |
1a5e3fb1 WN |
205 | { |
206 | struct bpf_object *obj; | |
207 | ||
208 | obj = calloc(1, sizeof(struct bpf_object) + strlen(path) + 1); | |
209 | if (!obj) { | |
210 | pr_warning("alloc memory failed for %s\n", path); | |
211 | return NULL; | |
212 | } | |
213 | ||
214 | strcpy(obj->path, path); | |
215 | obj->efile.fd = -1; | |
6c956392 WN |
216 | |
217 | /* | |
218 | * Caller of this function should also calls | |
219 | * bpf_object__elf_finish() after data collection to return | |
220 | * obj_buf to user. If not, we should duplicate the buffer to | |
221 | * avoid user freeing them before elf finish. | |
222 | */ | |
223 | obj->efile.obj_buf = obj_buf; | |
224 | obj->efile.obj_buf_sz = obj_buf_sz; | |
225 | ||
1a5e3fb1 WN |
226 | return obj; |
227 | } | |
228 | ||
229 | static void bpf_object__elf_finish(struct bpf_object *obj) | |
230 | { | |
231 | if (!obj_elf_valid(obj)) | |
232 | return; | |
233 | ||
234 | if (obj->efile.elf) { | |
235 | elf_end(obj->efile.elf); | |
236 | obj->efile.elf = NULL; | |
237 | } | |
bec7d68c | 238 | obj->efile.symbols = NULL; |
b62f06e8 WN |
239 | |
240 | zfree(&obj->efile.reloc); | |
241 | obj->efile.nr_reloc = 0; | |
1a5e3fb1 | 242 | zclose(obj->efile.fd); |
6c956392 WN |
243 | obj->efile.obj_buf = NULL; |
244 | obj->efile.obj_buf_sz = 0; | |
1a5e3fb1 WN |
245 | } |
246 | ||
247 | static int bpf_object__elf_init(struct bpf_object *obj) | |
248 | { | |
249 | int err = 0; | |
250 | GElf_Ehdr *ep; | |
251 | ||
252 | if (obj_elf_valid(obj)) { | |
253 | pr_warning("elf init: internal error\n"); | |
254 | return -EEXIST; | |
255 | } | |
256 | ||
6c956392 WN |
257 | if (obj->efile.obj_buf_sz > 0) { |
258 | /* | |
259 | * obj_buf should have been validated by | |
260 | * bpf_object__open_buffer(). | |
261 | */ | |
262 | obj->efile.elf = elf_memory(obj->efile.obj_buf, | |
263 | obj->efile.obj_buf_sz); | |
264 | } else { | |
265 | obj->efile.fd = open(obj->path, O_RDONLY); | |
266 | if (obj->efile.fd < 0) { | |
267 | pr_warning("failed to open %s: %s\n", obj->path, | |
268 | strerror(errno)); | |
269 | return -errno; | |
270 | } | |
271 | ||
272 | obj->efile.elf = elf_begin(obj->efile.fd, | |
273 | LIBBPF_ELF_C_READ_MMAP, | |
274 | NULL); | |
1a5e3fb1 WN |
275 | } |
276 | ||
1a5e3fb1 WN |
277 | if (!obj->efile.elf) { |
278 | pr_warning("failed to open %s as ELF file\n", | |
279 | obj->path); | |
280 | err = -EINVAL; | |
281 | goto errout; | |
282 | } | |
283 | ||
284 | if (!gelf_getehdr(obj->efile.elf, &obj->efile.ehdr)) { | |
285 | pr_warning("failed to get EHDR from %s\n", | |
286 | obj->path); | |
287 | err = -EINVAL; | |
288 | goto errout; | |
289 | } | |
290 | ep = &obj->efile.ehdr; | |
291 | ||
292 | if ((ep->e_type != ET_REL) || (ep->e_machine != 0)) { | |
293 | pr_warning("%s is not an eBPF object file\n", | |
294 | obj->path); | |
295 | err = -EINVAL; | |
296 | goto errout; | |
297 | } | |
298 | ||
299 | return 0; | |
300 | errout: | |
301 | bpf_object__elf_finish(obj); | |
302 | return err; | |
303 | } | |
304 | ||
cc4228d5 WN |
305 | static int |
306 | bpf_object__check_endianness(struct bpf_object *obj) | |
307 | { | |
308 | static unsigned int const endian = 1; | |
309 | ||
310 | switch (obj->efile.ehdr.e_ident[EI_DATA]) { | |
311 | case ELFDATA2LSB: | |
312 | /* We are big endian, BPF obj is little endian. */ | |
313 | if (*(unsigned char const *)&endian != 1) | |
314 | goto mismatch; | |
315 | break; | |
316 | ||
317 | case ELFDATA2MSB: | |
318 | /* We are little endian, BPF obj is big endian. */ | |
319 | if (*(unsigned char const *)&endian != 0) | |
320 | goto mismatch; | |
321 | break; | |
322 | default: | |
323 | return -EINVAL; | |
324 | } | |
325 | ||
326 | return 0; | |
327 | ||
328 | mismatch: | |
329 | pr_warning("Error: endianness mismatch.\n"); | |
330 | return -EINVAL; | |
331 | } | |
332 | ||
cb1e5e96 WN |
333 | static int |
334 | bpf_object__init_license(struct bpf_object *obj, | |
335 | void *data, size_t size) | |
336 | { | |
337 | memcpy(obj->license, data, | |
338 | min(size, sizeof(obj->license) - 1)); | |
339 | pr_debug("license of %s is %s\n", obj->path, obj->license); | |
340 | return 0; | |
341 | } | |
342 | ||
343 | static int | |
344 | bpf_object__init_kversion(struct bpf_object *obj, | |
345 | void *data, size_t size) | |
346 | { | |
347 | u32 kver; | |
348 | ||
349 | if (size != sizeof(kver)) { | |
350 | pr_warning("invalid kver section in %s\n", obj->path); | |
351 | return -EINVAL; | |
352 | } | |
353 | memcpy(&kver, data, sizeof(kver)); | |
354 | obj->kern_version = kver; | |
355 | pr_debug("kernel version of %s is %x\n", obj->path, | |
356 | obj->kern_version); | |
357 | return 0; | |
358 | } | |
359 | ||
0b3d1efa WN |
360 | static int |
361 | bpf_object__init_maps(struct bpf_object *obj, void *data, | |
362 | size_t size) | |
363 | { | |
364 | if (size == 0) { | |
365 | pr_debug("%s doesn't need map definition\n", | |
366 | obj->path); | |
367 | return 0; | |
368 | } | |
369 | ||
370 | obj->maps_buf = malloc(size); | |
371 | if (!obj->maps_buf) { | |
372 | pr_warning("malloc maps failed: %s\n", obj->path); | |
373 | return -ENOMEM; | |
374 | } | |
375 | ||
376 | obj->maps_buf_sz = size; | |
377 | memcpy(obj->maps_buf, data, size); | |
378 | pr_debug("maps in %s: %ld bytes\n", obj->path, (long)size); | |
379 | return 0; | |
380 | } | |
381 | ||
29603665 WN |
382 | static int bpf_object__elf_collect(struct bpf_object *obj) |
383 | { | |
384 | Elf *elf = obj->efile.elf; | |
385 | GElf_Ehdr *ep = &obj->efile.ehdr; | |
386 | Elf_Scn *scn = NULL; | |
387 | int idx = 0, err = 0; | |
388 | ||
389 | /* Elf is corrupted/truncated, avoid calling elf_strptr. */ | |
390 | if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) { | |
391 | pr_warning("failed to get e_shstrndx from %s\n", | |
392 | obj->path); | |
393 | return -EINVAL; | |
394 | } | |
395 | ||
396 | while ((scn = elf_nextscn(elf, scn)) != NULL) { | |
397 | char *name; | |
398 | GElf_Shdr sh; | |
399 | Elf_Data *data; | |
400 | ||
401 | idx++; | |
402 | if (gelf_getshdr(scn, &sh) != &sh) { | |
403 | pr_warning("failed to get section header from %s\n", | |
404 | obj->path); | |
405 | err = -EINVAL; | |
406 | goto out; | |
407 | } | |
408 | ||
409 | name = elf_strptr(elf, ep->e_shstrndx, sh.sh_name); | |
410 | if (!name) { | |
411 | pr_warning("failed to get section name from %s\n", | |
412 | obj->path); | |
413 | err = -EINVAL; | |
414 | goto out; | |
415 | } | |
416 | ||
417 | data = elf_getdata(scn, 0); | |
418 | if (!data) { | |
419 | pr_warning("failed to get section data from %s(%s)\n", | |
420 | name, obj->path); | |
421 | err = -EINVAL; | |
422 | goto out; | |
423 | } | |
424 | pr_debug("section %s, size %ld, link %d, flags %lx, type=%d\n", | |
425 | name, (unsigned long)data->d_size, | |
426 | (int)sh.sh_link, (unsigned long)sh.sh_flags, | |
427 | (int)sh.sh_type); | |
cb1e5e96 WN |
428 | |
429 | if (strcmp(name, "license") == 0) | |
430 | err = bpf_object__init_license(obj, | |
431 | data->d_buf, | |
432 | data->d_size); | |
433 | else if (strcmp(name, "version") == 0) | |
434 | err = bpf_object__init_kversion(obj, | |
435 | data->d_buf, | |
436 | data->d_size); | |
0b3d1efa WN |
437 | else if (strcmp(name, "maps") == 0) |
438 | err = bpf_object__init_maps(obj, data->d_buf, | |
439 | data->d_size); | |
bec7d68c WN |
440 | else if (sh.sh_type == SHT_SYMTAB) { |
441 | if (obj->efile.symbols) { | |
442 | pr_warning("bpf: multiple SYMTAB in %s\n", | |
443 | obj->path); | |
444 | err = -EEXIST; | |
445 | } else | |
446 | obj->efile.symbols = data; | |
a5b8bd47 WN |
447 | } else if ((sh.sh_type == SHT_PROGBITS) && |
448 | (sh.sh_flags & SHF_EXECINSTR) && | |
449 | (data->d_size > 0)) { | |
450 | err = bpf_object__add_program(obj, data->d_buf, | |
451 | data->d_size, name, idx); | |
452 | if (err) { | |
453 | char errmsg[128]; | |
454 | strerror_r(-err, errmsg, sizeof(errmsg)); | |
455 | pr_warning("failed to alloc program %s (%s): %s", | |
456 | name, obj->path, errmsg); | |
457 | } | |
b62f06e8 WN |
458 | } else if (sh.sh_type == SHT_REL) { |
459 | void *reloc = obj->efile.reloc; | |
460 | int nr_reloc = obj->efile.nr_reloc + 1; | |
461 | ||
462 | reloc = realloc(reloc, | |
463 | sizeof(*obj->efile.reloc) * nr_reloc); | |
464 | if (!reloc) { | |
465 | pr_warning("realloc failed\n"); | |
466 | err = -ENOMEM; | |
467 | } else { | |
468 | int n = nr_reloc - 1; | |
469 | ||
470 | obj->efile.reloc = reloc; | |
471 | obj->efile.nr_reloc = nr_reloc; | |
472 | ||
473 | obj->efile.reloc[n].shdr = sh; | |
474 | obj->efile.reloc[n].data = data; | |
475 | } | |
bec7d68c | 476 | } |
cb1e5e96 WN |
477 | if (err) |
478 | goto out; | |
29603665 WN |
479 | } |
480 | out: | |
481 | return err; | |
482 | } | |
483 | ||
cb1e5e96 WN |
484 | static int bpf_object__validate(struct bpf_object *obj) |
485 | { | |
486 | if (obj->kern_version == 0) { | |
487 | pr_warning("%s doesn't provide kernel version\n", | |
488 | obj->path); | |
489 | return -EINVAL; | |
490 | } | |
491 | return 0; | |
492 | } | |
493 | ||
1a5e3fb1 | 494 | static struct bpf_object * |
6c956392 | 495 | __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz) |
1a5e3fb1 WN |
496 | { |
497 | struct bpf_object *obj; | |
498 | ||
499 | if (elf_version(EV_CURRENT) == EV_NONE) { | |
500 | pr_warning("failed to init libelf for %s\n", path); | |
501 | return NULL; | |
502 | } | |
503 | ||
6c956392 | 504 | obj = bpf_object__new(path, obj_buf, obj_buf_sz); |
1a5e3fb1 WN |
505 | if (!obj) |
506 | return NULL; | |
507 | ||
508 | if (bpf_object__elf_init(obj)) | |
509 | goto out; | |
cc4228d5 WN |
510 | if (bpf_object__check_endianness(obj)) |
511 | goto out; | |
29603665 WN |
512 | if (bpf_object__elf_collect(obj)) |
513 | goto out; | |
cb1e5e96 WN |
514 | if (bpf_object__validate(obj)) |
515 | goto out; | |
1a5e3fb1 WN |
516 | |
517 | bpf_object__elf_finish(obj); | |
518 | return obj; | |
519 | out: | |
520 | bpf_object__close(obj); | |
521 | return NULL; | |
522 | } | |
523 | ||
524 | struct bpf_object *bpf_object__open(const char *path) | |
525 | { | |
526 | /* param validation */ | |
527 | if (!path) | |
528 | return NULL; | |
529 | ||
530 | pr_debug("loading %s\n", path); | |
531 | ||
6c956392 WN |
532 | return __bpf_object__open(path, NULL, 0); |
533 | } | |
534 | ||
535 | struct bpf_object *bpf_object__open_buffer(void *obj_buf, | |
536 | size_t obj_buf_sz) | |
537 | { | |
538 | /* param validation */ | |
539 | if (!obj_buf || obj_buf_sz <= 0) | |
540 | return NULL; | |
541 | ||
542 | pr_debug("loading object from buffer\n"); | |
543 | ||
544 | return __bpf_object__open("[buffer]", obj_buf, obj_buf_sz); | |
1a5e3fb1 WN |
545 | } |
546 | ||
547 | void bpf_object__close(struct bpf_object *obj) | |
548 | { | |
a5b8bd47 WN |
549 | size_t i; |
550 | ||
1a5e3fb1 WN |
551 | if (!obj) |
552 | return; | |
553 | ||
554 | bpf_object__elf_finish(obj); | |
555 | ||
0b3d1efa | 556 | zfree(&obj->maps_buf); |
a5b8bd47 WN |
557 | |
558 | if (obj->programs && obj->nr_programs) { | |
559 | for (i = 0; i < obj->nr_programs; i++) | |
560 | bpf_program__exit(&obj->programs[i]); | |
561 | } | |
562 | zfree(&obj->programs); | |
563 | ||
1a5e3fb1 WN |
564 | free(obj); |
565 | } |