]>
Commit | Line | Data |
---|---|---|
71bb428f JK |
1 | /* |
2 | * Copyright (C) 2017 Netronome Systems, Inc. | |
3 | * | |
4 | * This software is dual licensed under the GNU General License Version 2, | |
5 | * June 1991 as shown in the file COPYING in the top-level directory of this | |
6 | * source tree or the BSD 2-Clause License provided below. You have the | |
7 | * option to license this software under the complete terms of either license. | |
8 | * | |
9 | * The BSD 2-Clause License: | |
10 | * | |
11 | * Redistribution and use in source and binary forms, with or | |
12 | * without modification, are permitted provided that the following | |
13 | * conditions are met: | |
14 | * | |
15 | * 1. Redistributions of source code must retain the above | |
16 | * copyright notice, this list of conditions and the following | |
17 | * disclaimer. | |
18 | * | |
19 | * 2. Redistributions in binary form must reproduce the above | |
20 | * copyright notice, this list of conditions and the following | |
21 | * disclaimer in the documentation and/or other materials | |
22 | * provided with the distribution. | |
23 | * | |
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
31 | * SOFTWARE. | |
32 | */ | |
33 | ||
34 | /* Author: Jakub Kicinski <kubakici@wp.pl> */ | |
35 | ||
36 | #include <errno.h> | |
37 | #include <fcntl.h> | |
c9c35995 | 38 | #include <stdarg.h> |
71bb428f JK |
39 | #include <stdio.h> |
40 | #include <stdlib.h> | |
41 | #include <string.h> | |
42 | #include <time.h> | |
43 | #include <unistd.h> | |
44 | #include <sys/types.h> | |
45 | #include <sys/stat.h> | |
46 | ||
47 | #include <bpf.h> | |
48 | ||
49 | #include "main.h" | |
c9c35995 | 50 | #include "disasm.h" |
71bb428f JK |
51 | |
52 | static const char * const prog_type_name[] = { | |
53 | [BPF_PROG_TYPE_UNSPEC] = "unspec", | |
54 | [BPF_PROG_TYPE_SOCKET_FILTER] = "socket_filter", | |
55 | [BPF_PROG_TYPE_KPROBE] = "kprobe", | |
56 | [BPF_PROG_TYPE_SCHED_CLS] = "sched_cls", | |
57 | [BPF_PROG_TYPE_SCHED_ACT] = "sched_act", | |
58 | [BPF_PROG_TYPE_TRACEPOINT] = "tracepoint", | |
59 | [BPF_PROG_TYPE_XDP] = "xdp", | |
60 | [BPF_PROG_TYPE_PERF_EVENT] = "perf_event", | |
61 | [BPF_PROG_TYPE_CGROUP_SKB] = "cgroup_skb", | |
62 | [BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup_sock", | |
63 | [BPF_PROG_TYPE_LWT_IN] = "lwt_in", | |
64 | [BPF_PROG_TYPE_LWT_OUT] = "lwt_out", | |
65 | [BPF_PROG_TYPE_LWT_XMIT] = "lwt_xmit", | |
66 | [BPF_PROG_TYPE_SOCK_OPS] = "sock_ops", | |
67 | [BPF_PROG_TYPE_SK_SKB] = "sk_skb", | |
68 | }; | |
69 | ||
70 | static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) | |
71 | { | |
72 | struct timespec real_time_ts, boot_time_ts; | |
73 | time_t wallclock_secs; | |
74 | struct tm load_tm; | |
75 | ||
76 | buf[--size] = '\0'; | |
77 | ||
78 | if (clock_gettime(CLOCK_REALTIME, &real_time_ts) || | |
79 | clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) { | |
80 | perror("Can't read clocks"); | |
81 | snprintf(buf, size, "%llu", nsecs / 1000000000); | |
82 | return; | |
83 | } | |
84 | ||
85 | wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) + | |
86 | nsecs / 1000000000; | |
87 | ||
88 | if (!localtime_r(&wallclock_secs, &load_tm)) { | |
89 | snprintf(buf, size, "%llu", nsecs / 1000000000); | |
90 | return; | |
91 | } | |
92 | ||
93 | strftime(buf, size, "%b %d/%H:%M", &load_tm); | |
94 | } | |
95 | ||
96 | static int prog_fd_by_tag(unsigned char *tag) | |
97 | { | |
71bb428f JK |
98 | unsigned int id = 0; |
99 | int err; | |
100 | int fd; | |
101 | ||
102 | while (true) { | |
0ba23804 JO |
103 | struct bpf_prog_info info = {}; |
104 | __u32 len = sizeof(info); | |
105 | ||
71bb428f JK |
106 | err = bpf_prog_get_next_id(id, &id); |
107 | if (err) { | |
9a5ab8bf | 108 | p_err("%s", strerror(errno)); |
71bb428f JK |
109 | return -1; |
110 | } | |
111 | ||
112 | fd = bpf_prog_get_fd_by_id(id); | |
113 | if (fd < 0) { | |
9a5ab8bf QM |
114 | p_err("can't get prog by id (%u): %s", |
115 | id, strerror(errno)); | |
71bb428f JK |
116 | return -1; |
117 | } | |
118 | ||
119 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | |
120 | if (err) { | |
9a5ab8bf QM |
121 | p_err("can't get prog info (%u): %s", |
122 | id, strerror(errno)); | |
71bb428f JK |
123 | close(fd); |
124 | return -1; | |
125 | } | |
126 | ||
127 | if (!memcmp(tag, info.tag, BPF_TAG_SIZE)) | |
128 | return fd; | |
129 | ||
130 | close(fd); | |
131 | } | |
132 | } | |
133 | ||
134 | int prog_parse_fd(int *argc, char ***argv) | |
135 | { | |
136 | int fd; | |
137 | ||
138 | if (is_prefix(**argv, "id")) { | |
139 | unsigned int id; | |
140 | char *endptr; | |
141 | ||
142 | NEXT_ARGP(); | |
143 | ||
144 | id = strtoul(**argv, &endptr, 0); | |
145 | if (*endptr) { | |
9a5ab8bf | 146 | p_err("can't parse %s as ID", **argv); |
71bb428f JK |
147 | return -1; |
148 | } | |
149 | NEXT_ARGP(); | |
150 | ||
151 | fd = bpf_prog_get_fd_by_id(id); | |
152 | if (fd < 0) | |
9a5ab8bf | 153 | p_err("get by id (%u): %s", id, strerror(errno)); |
71bb428f JK |
154 | return fd; |
155 | } else if (is_prefix(**argv, "tag")) { | |
156 | unsigned char tag[BPF_TAG_SIZE]; | |
157 | ||
158 | NEXT_ARGP(); | |
159 | ||
160 | if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2, | |
161 | tag + 3, tag + 4, tag + 5, tag + 6, tag + 7) | |
162 | != BPF_TAG_SIZE) { | |
9a5ab8bf | 163 | p_err("can't parse tag"); |
71bb428f JK |
164 | return -1; |
165 | } | |
166 | NEXT_ARGP(); | |
167 | ||
168 | return prog_fd_by_tag(tag); | |
169 | } else if (is_prefix(**argv, "pinned")) { | |
170 | char *path; | |
171 | ||
172 | NEXT_ARGP(); | |
173 | ||
174 | path = **argv; | |
175 | NEXT_ARGP(); | |
176 | ||
177 | return open_obj_pinned_any(path, BPF_OBJ_PROG); | |
178 | } | |
179 | ||
9a5ab8bf | 180 | p_err("expected 'id', 'tag' or 'pinned', got: '%s'?", **argv); |
71bb428f JK |
181 | return -1; |
182 | } | |
183 | ||
184 | static void show_prog_maps(int fd, u32 num_maps) | |
185 | { | |
186 | struct bpf_prog_info info = {}; | |
187 | __u32 len = sizeof(info); | |
188 | __u32 map_ids[num_maps]; | |
189 | unsigned int i; | |
190 | int err; | |
191 | ||
192 | info.nr_map_ids = num_maps; | |
193 | info.map_ids = ptr_to_u64(map_ids); | |
194 | ||
195 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | |
196 | if (err || !info.nr_map_ids) | |
197 | return; | |
198 | ||
743cc665 QM |
199 | if (json_output) { |
200 | jsonw_name(json_wtr, "map_ids"); | |
201 | jsonw_start_array(json_wtr); | |
202 | for (i = 0; i < info.nr_map_ids; i++) | |
203 | jsonw_uint(json_wtr, map_ids[i]); | |
204 | jsonw_end_array(json_wtr); | |
205 | } else { | |
206 | printf(" map_ids "); | |
207 | for (i = 0; i < info.nr_map_ids; i++) | |
208 | printf("%u%s", map_ids[i], | |
209 | i == info.nr_map_ids - 1 ? "" : ","); | |
210 | } | |
71bb428f JK |
211 | } |
212 | ||
743cc665 | 213 | static void print_prog_json(struct bpf_prog_info *info, int fd) |
71bb428f | 214 | { |
71bb428f | 215 | char *memlock; |
71bb428f | 216 | |
743cc665 QM |
217 | jsonw_start_object(json_wtr); |
218 | jsonw_uint_field(json_wtr, "id", info->id); | |
219 | if (info->type < ARRAY_SIZE(prog_type_name)) | |
220 | jsonw_string_field(json_wtr, "type", | |
221 | prog_type_name[info->type]); | |
222 | else | |
223 | jsonw_uint_field(json_wtr, "type", info->type); | |
224 | ||
225 | if (*info->name) | |
226 | jsonw_string_field(json_wtr, "name", info->name); | |
227 | ||
228 | jsonw_name(json_wtr, "tag"); | |
229 | jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"", | |
230 | info->tag[0], info->tag[1], info->tag[2], info->tag[3], | |
231 | info->tag[4], info->tag[5], info->tag[6], info->tag[7]); | |
232 | ||
233 | if (info->load_time) { | |
234 | char buf[32]; | |
235 | ||
236 | print_boot_time(info->load_time, buf, sizeof(buf)); | |
237 | ||
238 | /* Piggy back on load_time, since 0 uid is a valid one */ | |
239 | jsonw_string_field(json_wtr, "loaded_at", buf); | |
240 | jsonw_uint_field(json_wtr, "uid", info->created_by_uid); | |
241 | } | |
242 | ||
243 | jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len); | |
244 | ||
245 | if (info->jited_prog_len) { | |
246 | jsonw_bool_field(json_wtr, "jited", true); | |
247 | jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len); | |
248 | } else { | |
249 | jsonw_bool_field(json_wtr, "jited", false); | |
71bb428f JK |
250 | } |
251 | ||
743cc665 QM |
252 | memlock = get_fdinfo(fd, "memlock"); |
253 | if (memlock) | |
254 | jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); | |
255 | free(memlock); | |
256 | ||
257 | if (info->nr_map_ids) | |
258 | show_prog_maps(fd, info->nr_map_ids); | |
259 | ||
4990f1f4 PB |
260 | if (!hash_empty(prog_table.table)) { |
261 | struct pinned_obj *obj; | |
262 | ||
263 | jsonw_name(json_wtr, "pinned"); | |
264 | jsonw_start_array(json_wtr); | |
265 | hash_for_each_possible(prog_table.table, obj, hash, info->id) { | |
266 | if (obj->id == info->id) | |
267 | jsonw_string(json_wtr, obj->path); | |
268 | } | |
269 | jsonw_end_array(json_wtr); | |
270 | } | |
271 | ||
743cc665 QM |
272 | jsonw_end_object(json_wtr); |
273 | } | |
274 | ||
275 | static void print_prog_plain(struct bpf_prog_info *info, int fd) | |
276 | { | |
277 | char *memlock; | |
278 | ||
279 | printf("%u: ", info->id); | |
280 | if (info->type < ARRAY_SIZE(prog_type_name)) | |
281 | printf("%s ", prog_type_name[info->type]); | |
71bb428f | 282 | else |
743cc665 | 283 | printf("type %u ", info->type); |
71bb428f | 284 | |
743cc665 QM |
285 | if (*info->name) |
286 | printf("name %s ", info->name); | |
71bb428f JK |
287 | |
288 | printf("tag "); | |
743cc665 | 289 | fprint_hex(stdout, info->tag, BPF_TAG_SIZE, ""); |
71bb428f JK |
290 | printf("\n"); |
291 | ||
743cc665 | 292 | if (info->load_time) { |
71bb428f JK |
293 | char buf[32]; |
294 | ||
743cc665 | 295 | print_boot_time(info->load_time, buf, sizeof(buf)); |
71bb428f JK |
296 | |
297 | /* Piggy back on load_time, since 0 uid is a valid one */ | |
743cc665 | 298 | printf("\tloaded_at %s uid %u\n", buf, info->created_by_uid); |
71bb428f JK |
299 | } |
300 | ||
743cc665 | 301 | printf("\txlated %uB", info->xlated_prog_len); |
71bb428f | 302 | |
743cc665 QM |
303 | if (info->jited_prog_len) |
304 | printf(" jited %uB", info->jited_prog_len); | |
71bb428f JK |
305 | else |
306 | printf(" not jited"); | |
307 | ||
308 | memlock = get_fdinfo(fd, "memlock"); | |
309 | if (memlock) | |
310 | printf(" memlock %sB", memlock); | |
311 | free(memlock); | |
312 | ||
743cc665 QM |
313 | if (info->nr_map_ids) |
314 | show_prog_maps(fd, info->nr_map_ids); | |
71bb428f | 315 | |
4990f1f4 PB |
316 | if (!hash_empty(prog_table.table)) { |
317 | struct pinned_obj *obj; | |
318 | ||
319 | printf("\n"); | |
320 | hash_for_each_possible(prog_table.table, obj, hash, info->id) { | |
321 | if (obj->id == info->id) | |
322 | printf("\tpinned %s\n", obj->path); | |
323 | } | |
324 | } | |
325 | ||
71bb428f | 326 | printf("\n"); |
743cc665 QM |
327 | } |
328 | ||
329 | static int show_prog(int fd) | |
330 | { | |
331 | struct bpf_prog_info info = {}; | |
332 | __u32 len = sizeof(info); | |
333 | int err; | |
334 | ||
335 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | |
336 | if (err) { | |
9a5ab8bf | 337 | p_err("can't get prog info: %s", strerror(errno)); |
743cc665 QM |
338 | return -1; |
339 | } | |
340 | ||
341 | if (json_output) | |
342 | print_prog_json(&info, fd); | |
343 | else | |
344 | print_prog_plain(&info, fd); | |
71bb428f JK |
345 | |
346 | return 0; | |
347 | } | |
348 | ||
349 | static int do_show(int argc, char **argv) | |
743cc665 QM |
350 | { |
351 | __u32 id = 0; | |
71bb428f JK |
352 | int err; |
353 | int fd; | |
354 | ||
c541b734 PB |
355 | if (show_pinned) |
356 | build_pinned_obj_table(&prog_table, BPF_OBJ_PROG); | |
4990f1f4 | 357 | |
71bb428f JK |
358 | if (argc == 2) { |
359 | fd = prog_parse_fd(&argc, &argv); | |
360 | if (fd < 0) | |
361 | return -1; | |
362 | ||
8a35b92f QM |
363 | err = show_prog(fd); |
364 | close(fd); | |
365 | return err; | |
71bb428f JK |
366 | } |
367 | ||
368 | if (argc) | |
369 | return BAD_ARG(); | |
370 | ||
743cc665 QM |
371 | if (json_output) |
372 | jsonw_start_array(json_wtr); | |
71bb428f JK |
373 | while (true) { |
374 | err = bpf_prog_get_next_id(id, &id); | |
375 | if (err) { | |
1739c26d QM |
376 | if (errno == ENOENT) { |
377 | err = 0; | |
71bb428f | 378 | break; |
1739c26d | 379 | } |
9a5ab8bf QM |
380 | p_err("can't get next program: %s%s", strerror(errno), |
381 | errno == EINVAL ? " -- kernel too old?" : ""); | |
743cc665 QM |
382 | err = -1; |
383 | break; | |
71bb428f JK |
384 | } |
385 | ||
386 | fd = bpf_prog_get_fd_by_id(id); | |
387 | if (fd < 0) { | |
8207c6dd JK |
388 | if (errno == ENOENT) |
389 | continue; | |
9a5ab8bf QM |
390 | p_err("can't get prog by id (%u): %s", |
391 | id, strerror(errno)); | |
743cc665 QM |
392 | err = -1; |
393 | break; | |
71bb428f JK |
394 | } |
395 | ||
396 | err = show_prog(fd); | |
397 | close(fd); | |
398 | if (err) | |
743cc665 | 399 | break; |
71bb428f JK |
400 | } |
401 | ||
743cc665 QM |
402 | if (json_output) |
403 | jsonw_end_array(json_wtr); | |
404 | ||
405 | return err; | |
71bb428f JK |
406 | } |
407 | ||
c9c35995 JK |
408 | static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...) |
409 | { | |
410 | va_list args; | |
411 | ||
412 | va_start(args, fmt); | |
413 | vprintf(fmt, args); | |
414 | va_end(args); | |
415 | } | |
416 | ||
f05e2c32 | 417 | static void dump_xlated_plain(void *buf, unsigned int len, bool opcodes) |
c9c35995 JK |
418 | { |
419 | struct bpf_insn *insn = buf; | |
9e2308c1 | 420 | bool double_insn = false; |
c9c35995 JK |
421 | unsigned int i; |
422 | ||
423 | for (i = 0; i < len / sizeof(*insn); i++) { | |
9e2308c1 QM |
424 | if (double_insn) { |
425 | double_insn = false; | |
426 | continue; | |
427 | } | |
428 | ||
429 | double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); | |
430 | ||
c9c35995 JK |
431 | printf("% 4d: ", i); |
432 | print_bpf_insn(print_insn, NULL, insn + i, true); | |
433 | ||
434 | if (opcodes) { | |
435 | printf(" "); | |
9cbe1f58 | 436 | fprint_hex(stdout, insn + i, 8, " "); |
9e2308c1 QM |
437 | if (double_insn && i < len - 1) { |
438 | printf(" "); | |
439 | fprint_hex(stdout, insn + i + 1, 8, " "); | |
440 | } | |
c9c35995 JK |
441 | printf("\n"); |
442 | } | |
c9c35995 JK |
443 | } |
444 | } | |
445 | ||
f05e2c32 QM |
446 | static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...) |
447 | { | |
448 | unsigned int l = strlen(fmt); | |
449 | char chomped_fmt[l]; | |
450 | va_list args; | |
451 | ||
452 | va_start(args, fmt); | |
453 | if (l > 0) { | |
454 | strncpy(chomped_fmt, fmt, l - 1); | |
455 | chomped_fmt[l - 1] = '\0'; | |
456 | } | |
457 | jsonw_vprintf_enquote(json_wtr, chomped_fmt, args); | |
458 | va_end(args); | |
459 | } | |
460 | ||
461 | static void dump_xlated_json(void *buf, unsigned int len, bool opcodes) | |
462 | { | |
463 | struct bpf_insn *insn = buf; | |
464 | bool double_insn = false; | |
465 | unsigned int i; | |
466 | ||
467 | jsonw_start_array(json_wtr); | |
468 | for (i = 0; i < len / sizeof(*insn); i++) { | |
469 | if (double_insn) { | |
470 | double_insn = false; | |
471 | continue; | |
472 | } | |
473 | double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); | |
474 | ||
475 | jsonw_start_object(json_wtr); | |
476 | jsonw_name(json_wtr, "disasm"); | |
477 | print_bpf_insn(print_insn_json, NULL, insn + i, true); | |
478 | ||
479 | if (opcodes) { | |
480 | jsonw_name(json_wtr, "opcodes"); | |
481 | jsonw_start_object(json_wtr); | |
482 | ||
483 | jsonw_name(json_wtr, "code"); | |
484 | jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code); | |
485 | ||
486 | jsonw_name(json_wtr, "src_reg"); | |
487 | jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg); | |
488 | ||
489 | jsonw_name(json_wtr, "dst_reg"); | |
490 | jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg); | |
491 | ||
492 | jsonw_name(json_wtr, "off"); | |
493 | print_hex_data_json((uint8_t *)(&insn[i].off), 2); | |
494 | ||
495 | jsonw_name(json_wtr, "imm"); | |
496 | if (double_insn && i < len - 1) | |
497 | print_hex_data_json((uint8_t *)(&insn[i].imm), | |
498 | 12); | |
499 | else | |
500 | print_hex_data_json((uint8_t *)(&insn[i].imm), | |
501 | 4); | |
502 | jsonw_end_object(json_wtr); | |
503 | } | |
504 | jsonw_end_object(json_wtr); | |
505 | } | |
506 | jsonw_end_array(json_wtr); | |
507 | } | |
508 | ||
71bb428f JK |
509 | static int do_dump(int argc, char **argv) |
510 | { | |
511 | struct bpf_prog_info info = {}; | |
512 | __u32 len = sizeof(info); | |
71bb428f JK |
513 | unsigned int buf_size; |
514 | char *filepath = NULL; | |
515 | bool opcodes = false; | |
516 | unsigned char *buf; | |
517 | __u32 *member_len; | |
518 | __u64 *member_ptr; | |
519 | ssize_t n; | |
520 | int err; | |
521 | int fd; | |
522 | ||
523 | if (is_prefix(*argv, "jited")) { | |
66155515 SF |
524 | if (disasm_init()) |
525 | return -1; | |
526 | ||
71bb428f JK |
527 | member_len = &info.jited_prog_len; |
528 | member_ptr = &info.jited_prog_insns; | |
71bb428f JK |
529 | } else if (is_prefix(*argv, "xlated")) { |
530 | member_len = &info.xlated_prog_len; | |
531 | member_ptr = &info.xlated_prog_insns; | |
532 | } else { | |
9a5ab8bf | 533 | p_err("expected 'xlated' or 'jited', got: %s", *argv); |
71bb428f JK |
534 | return -1; |
535 | } | |
536 | NEXT_ARG(); | |
537 | ||
538 | if (argc < 2) | |
539 | usage(); | |
540 | ||
541 | fd = prog_parse_fd(&argc, &argv); | |
542 | if (fd < 0) | |
543 | return -1; | |
544 | ||
545 | if (is_prefix(*argv, "file")) { | |
546 | NEXT_ARG(); | |
547 | if (!argc) { | |
9a5ab8bf | 548 | p_err("expected file path"); |
71bb428f JK |
549 | return -1; |
550 | } | |
551 | ||
552 | filepath = *argv; | |
553 | NEXT_ARG(); | |
554 | } else if (is_prefix(*argv, "opcodes")) { | |
555 | opcodes = true; | |
556 | NEXT_ARG(); | |
557 | } | |
558 | ||
71bb428f JK |
559 | if (argc) { |
560 | usage(); | |
561 | return -1; | |
562 | } | |
563 | ||
564 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | |
565 | if (err) { | |
9a5ab8bf | 566 | p_err("can't get prog info: %s", strerror(errno)); |
71bb428f JK |
567 | return -1; |
568 | } | |
569 | ||
570 | if (!*member_len) { | |
9a5ab8bf | 571 | p_info("no instructions returned"); |
71bb428f JK |
572 | close(fd); |
573 | return 0; | |
574 | } | |
575 | ||
576 | buf_size = *member_len; | |
577 | ||
578 | buf = malloc(buf_size); | |
579 | if (!buf) { | |
9a5ab8bf | 580 | p_err("mem alloc failed"); |
71bb428f JK |
581 | close(fd); |
582 | return -1; | |
583 | } | |
584 | ||
585 | memset(&info, 0, sizeof(info)); | |
586 | ||
587 | *member_ptr = ptr_to_u64(buf); | |
588 | *member_len = buf_size; | |
589 | ||
590 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | |
591 | close(fd); | |
592 | if (err) { | |
9a5ab8bf | 593 | p_err("can't get prog info: %s", strerror(errno)); |
71bb428f JK |
594 | goto err_free; |
595 | } | |
596 | ||
597 | if (*member_len > buf_size) { | |
9a5ab8bf | 598 | p_err("too many instructions returned"); |
71bb428f JK |
599 | goto err_free; |
600 | } | |
601 | ||
602 | if (filepath) { | |
603 | fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); | |
604 | if (fd < 0) { | |
9a5ab8bf QM |
605 | p_err("can't open file %s: %s", filepath, |
606 | strerror(errno)); | |
71bb428f JK |
607 | goto err_free; |
608 | } | |
609 | ||
610 | n = write(fd, buf, *member_len); | |
611 | close(fd); | |
612 | if (n != *member_len) { | |
9a5ab8bf QM |
613 | p_err("error writing output file: %s", |
614 | n < 0 ? strerror(errno) : "short write"); | |
71bb428f JK |
615 | goto err_free; |
616 | } | |
617 | } else { | |
c9c35995 JK |
618 | if (member_len == &info.jited_prog_len) |
619 | disasm_print_insn(buf, *member_len, opcodes); | |
620 | else | |
f05e2c32 QM |
621 | if (json_output) |
622 | dump_xlated_json(buf, *member_len, opcodes); | |
623 | else | |
624 | dump_xlated_plain(buf, *member_len, opcodes); | |
71bb428f JK |
625 | } |
626 | ||
627 | free(buf); | |
628 | ||
629 | return 0; | |
630 | ||
631 | err_free: | |
632 | free(buf); | |
633 | return -1; | |
634 | } | |
635 | ||
636 | static int do_pin(int argc, char **argv) | |
637 | { | |
004b45c0 QM |
638 | int err; |
639 | ||
640 | err = do_pin_any(argc, argv, bpf_prog_get_fd_by_id); | |
641 | if (!err && json_output) | |
642 | jsonw_null(json_wtr); | |
643 | return err; | |
71bb428f JK |
644 | } |
645 | ||
646 | static int do_help(int argc, char **argv) | |
647 | { | |
004b45c0 QM |
648 | if (json_output) { |
649 | jsonw_null(json_wtr); | |
650 | return 0; | |
651 | } | |
652 | ||
71bb428f JK |
653 | fprintf(stderr, |
654 | "Usage: %s %s show [PROG]\n" | |
8dfbc6d1 QM |
655 | " %s %s dump xlated PROG [{ file FILE | opcodes }]\n" |
656 | " %s %s dump jited PROG [{ file FILE | opcodes }]\n" | |
71bb428f JK |
657 | " %s %s pin PROG FILE\n" |
658 | " %s %s help\n" | |
659 | "\n" | |
660 | " " HELP_SPEC_PROGRAM "\n" | |
0641c3c8 | 661 | " " HELP_SPEC_OPTIONS "\n" |
71bb428f JK |
662 | "", |
663 | bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], | |
664 | bin_name, argv[-2], bin_name, argv[-2]); | |
665 | ||
666 | return 0; | |
667 | } | |
668 | ||
669 | static const struct cmd cmds[] = { | |
670 | { "show", do_show }, | |
9f606179 | 671 | { "help", do_help }, |
71bb428f JK |
672 | { "dump", do_dump }, |
673 | { "pin", do_pin }, | |
674 | { 0 } | |
675 | }; | |
676 | ||
677 | int do_prog(int argc, char **argv) | |
678 | { | |
679 | return cmd_select(cmds, argc, argv, do_help); | |
680 | } |