]>
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 <[email protected]> */ | |
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> | |
928631e0 | 44 | #include <net/if.h> |
71bb428f JK |
45 | #include <sys/types.h> |
46 | #include <sys/stat.h> | |
47 | ||
48 | #include <bpf.h> | |
49 | ||
50 | #include "main.h" | |
c9c35995 | 51 | #include "disasm.h" |
71bb428f JK |
52 | |
53 | static const char * const prog_type_name[] = { | |
54 | [BPF_PROG_TYPE_UNSPEC] = "unspec", | |
55 | [BPF_PROG_TYPE_SOCKET_FILTER] = "socket_filter", | |
56 | [BPF_PROG_TYPE_KPROBE] = "kprobe", | |
57 | [BPF_PROG_TYPE_SCHED_CLS] = "sched_cls", | |
58 | [BPF_PROG_TYPE_SCHED_ACT] = "sched_act", | |
59 | [BPF_PROG_TYPE_TRACEPOINT] = "tracepoint", | |
60 | [BPF_PROG_TYPE_XDP] = "xdp", | |
61 | [BPF_PROG_TYPE_PERF_EVENT] = "perf_event", | |
62 | [BPF_PROG_TYPE_CGROUP_SKB] = "cgroup_skb", | |
63 | [BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup_sock", | |
64 | [BPF_PROG_TYPE_LWT_IN] = "lwt_in", | |
65 | [BPF_PROG_TYPE_LWT_OUT] = "lwt_out", | |
66 | [BPF_PROG_TYPE_LWT_XMIT] = "lwt_xmit", | |
67 | [BPF_PROG_TYPE_SOCK_OPS] = "sock_ops", | |
68 | [BPF_PROG_TYPE_SK_SKB] = "sk_skb", | |
69 | }; | |
70 | ||
71 | static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) | |
72 | { | |
73 | struct timespec real_time_ts, boot_time_ts; | |
74 | time_t wallclock_secs; | |
75 | struct tm load_tm; | |
76 | ||
77 | buf[--size] = '\0'; | |
78 | ||
79 | if (clock_gettime(CLOCK_REALTIME, &real_time_ts) || | |
80 | clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) { | |
81 | perror("Can't read clocks"); | |
82 | snprintf(buf, size, "%llu", nsecs / 1000000000); | |
83 | return; | |
84 | } | |
85 | ||
86 | wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) + | |
87 | nsecs / 1000000000; | |
88 | ||
89 | if (!localtime_r(&wallclock_secs, &load_tm)) { | |
90 | snprintf(buf, size, "%llu", nsecs / 1000000000); | |
91 | return; | |
92 | } | |
93 | ||
94 | strftime(buf, size, "%b %d/%H:%M", &load_tm); | |
95 | } | |
96 | ||
97 | static int prog_fd_by_tag(unsigned char *tag) | |
98 | { | |
99 | struct bpf_prog_info info = {}; | |
100 | __u32 len = sizeof(info); | |
101 | unsigned int id = 0; | |
102 | int err; | |
103 | int fd; | |
104 | ||
105 | while (true) { | |
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 | ||
928631e0 JK |
233 | if (info->status & BPF_PROG_STATUS_DEV_BOUND) { |
234 | jsonw_name(json_wtr, "dev"); | |
235 | if (info->ifindex) { | |
236 | char name[IF_NAMESIZE]; | |
237 | ||
238 | if (!if_indextoname(info->ifindex, name)) | |
239 | jsonw_printf(json_wtr, "\"ifindex:%d\"", | |
240 | info->ifindex); | |
241 | else | |
242 | jsonw_printf(json_wtr, "\"%s\"", name); | |
243 | } else { | |
244 | jsonw_printf(json_wtr, "\"unknown\""); | |
245 | } | |
246 | } | |
247 | ||
743cc665 QM |
248 | if (info->load_time) { |
249 | char buf[32]; | |
250 | ||
251 | print_boot_time(info->load_time, buf, sizeof(buf)); | |
252 | ||
253 | /* Piggy back on load_time, since 0 uid is a valid one */ | |
254 | jsonw_string_field(json_wtr, "loaded_at", buf); | |
255 | jsonw_uint_field(json_wtr, "uid", info->created_by_uid); | |
256 | } | |
257 | ||
258 | jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len); | |
259 | ||
260 | if (info->jited_prog_len) { | |
261 | jsonw_bool_field(json_wtr, "jited", true); | |
262 | jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len); | |
263 | } else { | |
264 | jsonw_bool_field(json_wtr, "jited", false); | |
71bb428f JK |
265 | } |
266 | ||
743cc665 QM |
267 | memlock = get_fdinfo(fd, "memlock"); |
268 | if (memlock) | |
269 | jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); | |
270 | free(memlock); | |
271 | ||
272 | if (info->nr_map_ids) | |
273 | show_prog_maps(fd, info->nr_map_ids); | |
274 | ||
275 | jsonw_end_object(json_wtr); | |
276 | } | |
277 | ||
278 | static void print_prog_plain(struct bpf_prog_info *info, int fd) | |
279 | { | |
280 | char *memlock; | |
281 | ||
282 | printf("%u: ", info->id); | |
283 | if (info->type < ARRAY_SIZE(prog_type_name)) | |
284 | printf("%s ", prog_type_name[info->type]); | |
71bb428f | 285 | else |
743cc665 | 286 | printf("type %u ", info->type); |
71bb428f | 287 | |
743cc665 QM |
288 | if (*info->name) |
289 | printf("name %s ", info->name); | |
71bb428f JK |
290 | |
291 | printf("tag "); | |
743cc665 | 292 | fprint_hex(stdout, info->tag, BPF_TAG_SIZE, ""); |
928631e0 JK |
293 | printf(" "); |
294 | ||
295 | if (info->status & BPF_PROG_STATUS_DEV_BOUND) { | |
296 | printf("dev "); | |
297 | if (info->ifindex) { | |
298 | char name[IF_NAMESIZE]; | |
299 | ||
300 | if (!if_indextoname(info->ifindex, name)) | |
301 | printf("ifindex:%d ", info->ifindex); | |
302 | else | |
303 | printf("%s ", name); | |
304 | } else { | |
305 | printf("unknown "); | |
306 | } | |
307 | } | |
71bb428f JK |
308 | printf("\n"); |
309 | ||
743cc665 | 310 | if (info->load_time) { |
71bb428f JK |
311 | char buf[32]; |
312 | ||
743cc665 | 313 | print_boot_time(info->load_time, buf, sizeof(buf)); |
71bb428f JK |
314 | |
315 | /* Piggy back on load_time, since 0 uid is a valid one */ | |
743cc665 | 316 | printf("\tloaded_at %s uid %u\n", buf, info->created_by_uid); |
71bb428f JK |
317 | } |
318 | ||
743cc665 | 319 | printf("\txlated %uB", info->xlated_prog_len); |
71bb428f | 320 | |
743cc665 QM |
321 | if (info->jited_prog_len) |
322 | printf(" jited %uB", info->jited_prog_len); | |
71bb428f JK |
323 | else |
324 | printf(" not jited"); | |
325 | ||
326 | memlock = get_fdinfo(fd, "memlock"); | |
327 | if (memlock) | |
328 | printf(" memlock %sB", memlock); | |
329 | free(memlock); | |
330 | ||
743cc665 QM |
331 | if (info->nr_map_ids) |
332 | show_prog_maps(fd, info->nr_map_ids); | |
71bb428f JK |
333 | |
334 | printf("\n"); | |
743cc665 QM |
335 | } |
336 | ||
337 | static int show_prog(int fd) | |
338 | { | |
339 | struct bpf_prog_info info = {}; | |
340 | __u32 len = sizeof(info); | |
341 | int err; | |
342 | ||
343 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | |
344 | if (err) { | |
9a5ab8bf | 345 | p_err("can't get prog info: %s", strerror(errno)); |
743cc665 QM |
346 | return -1; |
347 | } | |
348 | ||
349 | if (json_output) | |
350 | print_prog_json(&info, fd); | |
351 | else | |
352 | print_prog_plain(&info, fd); | |
71bb428f JK |
353 | |
354 | return 0; | |
355 | } | |
356 | ||
357 | static int do_show(int argc, char **argv) | |
743cc665 QM |
358 | { |
359 | __u32 id = 0; | |
71bb428f JK |
360 | int err; |
361 | int fd; | |
362 | ||
363 | if (argc == 2) { | |
364 | fd = prog_parse_fd(&argc, &argv); | |
365 | if (fd < 0) | |
366 | return -1; | |
367 | ||
368 | return show_prog(fd); | |
369 | } | |
370 | ||
371 | if (argc) | |
372 | return BAD_ARG(); | |
373 | ||
743cc665 QM |
374 | if (json_output) |
375 | jsonw_start_array(json_wtr); | |
71bb428f JK |
376 | while (true) { |
377 | err = bpf_prog_get_next_id(id, &id); | |
378 | if (err) { | |
1739c26d QM |
379 | if (errno == ENOENT) { |
380 | err = 0; | |
71bb428f | 381 | break; |
1739c26d | 382 | } |
9a5ab8bf QM |
383 | p_err("can't get next program: %s%s", strerror(errno), |
384 | errno == EINVAL ? " -- kernel too old?" : ""); | |
743cc665 QM |
385 | err = -1; |
386 | break; | |
71bb428f JK |
387 | } |
388 | ||
389 | fd = bpf_prog_get_fd_by_id(id); | |
390 | if (fd < 0) { | |
9a5ab8bf QM |
391 | p_err("can't get prog by id (%u): %s", |
392 | id, strerror(errno)); | |
743cc665 QM |
393 | err = -1; |
394 | break; | |
71bb428f JK |
395 | } |
396 | ||
397 | err = show_prog(fd); | |
398 | close(fd); | |
399 | if (err) | |
743cc665 | 400 | break; |
71bb428f JK |
401 | } |
402 | ||
743cc665 QM |
403 | if (json_output) |
404 | jsonw_end_array(json_wtr); | |
405 | ||
406 | return err; | |
71bb428f JK |
407 | } |
408 | ||
c9c35995 JK |
409 | static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...) |
410 | { | |
411 | va_list args; | |
412 | ||
413 | va_start(args, fmt); | |
414 | vprintf(fmt, args); | |
415 | va_end(args); | |
416 | } | |
417 | ||
f05e2c32 | 418 | static void dump_xlated_plain(void *buf, unsigned int len, bool opcodes) |
c9c35995 JK |
419 | { |
420 | struct bpf_insn *insn = buf; | |
9e2308c1 | 421 | bool double_insn = false; |
c9c35995 JK |
422 | unsigned int i; |
423 | ||
424 | for (i = 0; i < len / sizeof(*insn); i++) { | |
9e2308c1 QM |
425 | if (double_insn) { |
426 | double_insn = false; | |
427 | continue; | |
428 | } | |
429 | ||
430 | double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); | |
431 | ||
c9c35995 JK |
432 | printf("% 4d: ", i); |
433 | print_bpf_insn(print_insn, NULL, insn + i, true); | |
434 | ||
435 | if (opcodes) { | |
436 | printf(" "); | |
9cbe1f58 | 437 | fprint_hex(stdout, insn + i, 8, " "); |
9e2308c1 QM |
438 | if (double_insn && i < len - 1) { |
439 | printf(" "); | |
440 | fprint_hex(stdout, insn + i + 1, 8, " "); | |
441 | } | |
c9c35995 JK |
442 | printf("\n"); |
443 | } | |
c9c35995 JK |
444 | } |
445 | } | |
446 | ||
f05e2c32 QM |
447 | static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...) |
448 | { | |
449 | unsigned int l = strlen(fmt); | |
450 | char chomped_fmt[l]; | |
451 | va_list args; | |
452 | ||
453 | va_start(args, fmt); | |
454 | if (l > 0) { | |
455 | strncpy(chomped_fmt, fmt, l - 1); | |
456 | chomped_fmt[l - 1] = '\0'; | |
457 | } | |
458 | jsonw_vprintf_enquote(json_wtr, chomped_fmt, args); | |
459 | va_end(args); | |
460 | } | |
461 | ||
462 | static void dump_xlated_json(void *buf, unsigned int len, bool opcodes) | |
463 | { | |
464 | struct bpf_insn *insn = buf; | |
465 | bool double_insn = false; | |
466 | unsigned int i; | |
467 | ||
468 | jsonw_start_array(json_wtr); | |
469 | for (i = 0; i < len / sizeof(*insn); i++) { | |
470 | if (double_insn) { | |
471 | double_insn = false; | |
472 | continue; | |
473 | } | |
474 | double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); | |
475 | ||
476 | jsonw_start_object(json_wtr); | |
477 | jsonw_name(json_wtr, "disasm"); | |
478 | print_bpf_insn(print_insn_json, NULL, insn + i, true); | |
479 | ||
480 | if (opcodes) { | |
481 | jsonw_name(json_wtr, "opcodes"); | |
482 | jsonw_start_object(json_wtr); | |
483 | ||
484 | jsonw_name(json_wtr, "code"); | |
485 | jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code); | |
486 | ||
487 | jsonw_name(json_wtr, "src_reg"); | |
488 | jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg); | |
489 | ||
490 | jsonw_name(json_wtr, "dst_reg"); | |
491 | jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg); | |
492 | ||
493 | jsonw_name(json_wtr, "off"); | |
494 | print_hex_data_json((uint8_t *)(&insn[i].off), 2); | |
495 | ||
496 | jsonw_name(json_wtr, "imm"); | |
497 | if (double_insn && i < len - 1) | |
498 | print_hex_data_json((uint8_t *)(&insn[i].imm), | |
499 | 12); | |
500 | else | |
501 | print_hex_data_json((uint8_t *)(&insn[i].imm), | |
502 | 4); | |
503 | jsonw_end_object(json_wtr); | |
504 | } | |
505 | jsonw_end_object(json_wtr); | |
506 | } | |
507 | jsonw_end_array(json_wtr); | |
508 | } | |
509 | ||
71bb428f JK |
510 | static int do_dump(int argc, char **argv) |
511 | { | |
512 | struct bpf_prog_info info = {}; | |
513 | __u32 len = sizeof(info); | |
71bb428f JK |
514 | unsigned int buf_size; |
515 | char *filepath = NULL; | |
516 | bool opcodes = false; | |
517 | unsigned char *buf; | |
518 | __u32 *member_len; | |
519 | __u64 *member_ptr; | |
520 | ssize_t n; | |
521 | int err; | |
522 | int fd; | |
523 | ||
524 | if (is_prefix(*argv, "jited")) { | |
525 | member_len = &info.jited_prog_len; | |
526 | member_ptr = &info.jited_prog_insns; | |
71bb428f JK |
527 | } else if (is_prefix(*argv, "xlated")) { |
528 | member_len = &info.xlated_prog_len; | |
529 | member_ptr = &info.xlated_prog_insns; | |
530 | } else { | |
9a5ab8bf | 531 | p_err("expected 'xlated' or 'jited', got: %s", *argv); |
71bb428f JK |
532 | return -1; |
533 | } | |
534 | NEXT_ARG(); | |
535 | ||
536 | if (argc < 2) | |
537 | usage(); | |
538 | ||
539 | fd = prog_parse_fd(&argc, &argv); | |
540 | if (fd < 0) | |
541 | return -1; | |
542 | ||
543 | if (is_prefix(*argv, "file")) { | |
544 | NEXT_ARG(); | |
545 | if (!argc) { | |
9a5ab8bf | 546 | p_err("expected file path"); |
71bb428f JK |
547 | return -1; |
548 | } | |
549 | ||
550 | filepath = *argv; | |
551 | NEXT_ARG(); | |
552 | } else if (is_prefix(*argv, "opcodes")) { | |
553 | opcodes = true; | |
554 | NEXT_ARG(); | |
555 | } | |
556 | ||
71bb428f JK |
557 | if (argc) { |
558 | usage(); | |
559 | return -1; | |
560 | } | |
561 | ||
562 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | |
563 | if (err) { | |
9a5ab8bf | 564 | p_err("can't get prog info: %s", strerror(errno)); |
71bb428f JK |
565 | return -1; |
566 | } | |
567 | ||
568 | if (!*member_len) { | |
9a5ab8bf | 569 | p_info("no instructions returned"); |
71bb428f JK |
570 | close(fd); |
571 | return 0; | |
572 | } | |
573 | ||
574 | buf_size = *member_len; | |
575 | ||
576 | buf = malloc(buf_size); | |
577 | if (!buf) { | |
9a5ab8bf | 578 | p_err("mem alloc failed"); |
71bb428f JK |
579 | close(fd); |
580 | return -1; | |
581 | } | |
582 | ||
583 | memset(&info, 0, sizeof(info)); | |
584 | ||
585 | *member_ptr = ptr_to_u64(buf); | |
586 | *member_len = buf_size; | |
587 | ||
588 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | |
589 | close(fd); | |
590 | if (err) { | |
9a5ab8bf | 591 | p_err("can't get prog info: %s", strerror(errno)); |
71bb428f JK |
592 | goto err_free; |
593 | } | |
594 | ||
595 | if (*member_len > buf_size) { | |
9a5ab8bf | 596 | p_err("too many instructions returned"); |
71bb428f JK |
597 | goto err_free; |
598 | } | |
599 | ||
600 | if (filepath) { | |
601 | fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); | |
602 | if (fd < 0) { | |
9a5ab8bf QM |
603 | p_err("can't open file %s: %s", filepath, |
604 | strerror(errno)); | |
71bb428f JK |
605 | goto err_free; |
606 | } | |
607 | ||
608 | n = write(fd, buf, *member_len); | |
609 | close(fd); | |
610 | if (n != *member_len) { | |
9a5ab8bf QM |
611 | p_err("error writing output file: %s", |
612 | n < 0 ? strerror(errno) : "short write"); | |
71bb428f JK |
613 | goto err_free; |
614 | } | |
615 | } else { | |
c9c35995 JK |
616 | if (member_len == &info.jited_prog_len) |
617 | disasm_print_insn(buf, *member_len, opcodes); | |
618 | else | |
f05e2c32 QM |
619 | if (json_output) |
620 | dump_xlated_json(buf, *member_len, opcodes); | |
621 | else | |
622 | dump_xlated_plain(buf, *member_len, opcodes); | |
71bb428f JK |
623 | } |
624 | ||
625 | free(buf); | |
626 | ||
627 | return 0; | |
628 | ||
629 | err_free: | |
630 | free(buf); | |
631 | return -1; | |
632 | } | |
633 | ||
634 | static int do_pin(int argc, char **argv) | |
635 | { | |
004b45c0 QM |
636 | int err; |
637 | ||
638 | err = do_pin_any(argc, argv, bpf_prog_get_fd_by_id); | |
639 | if (!err && json_output) | |
640 | jsonw_null(json_wtr); | |
641 | return err; | |
71bb428f JK |
642 | } |
643 | ||
644 | static int do_help(int argc, char **argv) | |
645 | { | |
004b45c0 QM |
646 | if (json_output) { |
647 | jsonw_null(json_wtr); | |
648 | return 0; | |
649 | } | |
650 | ||
71bb428f JK |
651 | fprintf(stderr, |
652 | "Usage: %s %s show [PROG]\n" | |
8dfbc6d1 QM |
653 | " %s %s dump xlated PROG [{ file FILE | opcodes }]\n" |
654 | " %s %s dump jited PROG [{ file FILE | opcodes }]\n" | |
71bb428f JK |
655 | " %s %s pin PROG FILE\n" |
656 | " %s %s help\n" | |
657 | "\n" | |
658 | " " HELP_SPEC_PROGRAM "\n" | |
0641c3c8 | 659 | " " HELP_SPEC_OPTIONS "\n" |
71bb428f JK |
660 | "", |
661 | bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], | |
662 | bin_name, argv[-2], bin_name, argv[-2]); | |
663 | ||
664 | return 0; | |
665 | } | |
666 | ||
667 | static const struct cmd cmds[] = { | |
668 | { "show", do_show }, | |
9f606179 | 669 | { "help", do_help }, |
71bb428f JK |
670 | { "dump", do_dump }, |
671 | { "pin", do_pin }, | |
672 | { 0 } | |
673 | }; | |
674 | ||
675 | int do_prog(int argc, char **argv) | |
676 | { | |
677 | return cmd_select(cmds, argc, argv, do_help); | |
678 | } |