History

Merge branch 'bpftool-show-pid'

Andrii Nakryiko says: ==================== This patch set implements libbpf support for a second kind of special externs, kernel symbols, in addition to existing Kconfig externs. Right now, only untyped (const void) externs are supported, which, in C language, allow only to take their address. In the future, with kernel BTF getting type info about its own global and per-cpu variables, libbpf will extend this support with BTF type info, which will allow to also directly access variable's contents and follow its internal pointers, similarly to how it's possible today in fentry/fexit programs. As a first practical use of this functionality, bpftool gained ability to show PIDs of processes that have open file descriptors for BPF map/program/link/BTF object. It relies on iter/task_file BPF iterator program to extract this information efficiently. There was a bunch of bpftool refactoring (especially Makefile) necessary to generalize bpftool's internal BPF program use. This includes generalization of BPF skeletons support, addition of a vmlinux.h generation, extracting and building minimal subset of bpftool for bootstrapping. v2->v3: - fix sec_btf_id check (Hao); v1->v2: - docs fixes (Quentin); - dual GPL/BSD license for pid_inter.bpf.c (Quentin); - NULL-init kcfg_data (Hao Luo); rfc->v1: - show pids, if supported by kernel, always (Alexei); - switched iter output to binary to support showing process names; - update man pages; - fix few minor bugs in libbpf w.r.t. extern iteration. ====================

Alexei Starovoitov committed 6 years ago

Commit b3eece02 parents 1bdb6c9 075c77627 files · +1252 −547Browse files

Unified · Split

27 files
  1. tools/bpf/bpftool/.gitignore +3 −2
  2. tools/bpf/bpftool/Documentation/bpftool-btf.rst +5 −0
  3. tools/bpf/bpftool/Documentation/bpftool-link.rst +12 −1
  4. tools/bpf/bpftool/Documentation/bpftool-map.rst +7 −1
  5. tools/bpf/bpftool/Documentation/bpftool-prog.rst +11 −0
  6. tools/bpf/bpftool/Makefile +39 −21
  7. tools/bpf/bpftool/btf.c +6 −0
  8. tools/bpf/bpftool/common.c +308 −0
  9. tools/bpf/bpftool/link.c +7 −0
  10. tools/bpf/bpftool/main.c +10 −2
  11. tools/bpf/bpftool/main.h +44 −12
  12. tools/bpf/bpftool/map.c +7 −156
  13. tools/bpf/bpftool/pids.c +229 −0
  14. tools/bpf/bpftool/prog.c +7 −152
  15. tools/bpf/bpftool/skeleton/pid_iter.bpf.c +80 −0
  16. tools/bpf/bpftool/skeleton/pid_iter.h +12 −0
  17. tools/bpf/bpftool/skeleton/profiler.bpf.c +1 −2
  18. tools/bpf/bpftool/skeleton/profiler.h +0 −46
  19. tools/build/feature/Makefile +2 −2
  20. tools/build/feature/test-clang-bpf-co-re.c +9 −0
  21. tools/build/feature/test-clang-bpf-global-var.c +0 −4
  22. tools/lib/bpf/bpf_core_read.h +4 −4
  23. tools/lib/bpf/bpf_helpers.h +1 −0
  24. tools/lib/bpf/btf.h +5 −0
  25. tools/lib/bpf/libbpf.c +340 −142
  26. tools/testing/selftests/bpf/prog_tests/ksyms.c +71 −0
  27. tools/testing/selftests/bpf/progs/test_ksyms.c +32 −0
tools/bpf/bpftool/.gitignore+3−2
@@ -1,10 +1,11 @@
11 # SPDX-License-Identifier: GPL-2.0-only
22 *.d
3−/_bpftool
3+/bpftool-bootstrap
44 /bpftool
55 bpftool*.8
66 bpf-helpers.*
77 FEATURE-DUMP.bpftool
88 feature
99 libbpf
10−profiler.skel.h
10+/*.skel.h
11+/vmlinux.h
tools/bpf/bpftool/Documentation/bpftool-btf.rst+5−0
@@ -36,6 +36,11 @@ DESCRIPTION · 35 unmodified lines
3636 otherwise list all BTF objects currently loaded on the
3737 system.
3838
39+ Since Linux 5.8 bpftool is able to discover information about
40+ processes that hold open file descriptors (FDs) against BTF
41+ objects. On such kernels bpftool will automatically emit this
42+ information as well.
43+
3944 **bpftool btf dump** *BTF_SRC*
4045 Dump BTF entries from a given *BTF_SRC*.
4146
tools/bpf/bpftool/Documentation/bpftool-link.rst+12−1
@@ -37,6 +37,11 @@ DESCRIPTION · 36 unmodified lines
3737 zero or more named attributes, some of which depend on type
3838 of link.
3939
40+ Since Linux 5.8 bpftool is able to discover information about
41+ processes that hold open file descriptors (FDs) against BPF
42+ links. On such kernels bpftool will automatically emit this
43+ information as well.
44+
4045 **bpftool link pin** *LINK* *FILE*
4146 Pin link *LINK* as *FILE*.
4247
@@ -82,6 +87,7 @@ EXAMPLES · 39 unmodified lines
8287
8388 10: cgroup prog 25
8489 cgroup_id 614 attach_type egress
90+ pids test_progs(223)
8591
8692 **# bpftool --json --pretty link show**
8793
@@ -91,7 +97,12 @@ EXAMPLES · 3 unmodified lines
9197 "type": "cgroup",
9298 "prog_id": 25,
9399 "cgroup_id": 614,
94− "attach_type": "egress"
100+ "attach_type": "egress",
101+ "pids": [{
102+ "pid": 223,
103+ "comm": "test_progs"
104+ }
105+ ]
95106 }
96107 ]
97108
tools/bpf/bpftool/Documentation/bpftool-map.rst+7−1
@@ -62,6 +62,11 @@ DESCRIPTION · 61 unmodified lines
6262 Output will start with map ID followed by map type and
6363 zero or more named attributes (depending on kernel version).
6464
65+ Since Linux 5.8 bpftool is able to discover information about
66+ processes that hold open file descriptors (FDs) against BPF
67+ maps. On such kernels bpftool will automatically emit this
68+ information as well.
69+
6570 **bpftool map create** *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**dev** *NAME*]
6671 Create a new map with given parameters and pin it to *bpffs*
6772 as *FILE*.
@@ -180,7 +185,8 @@ EXAMPLES · 112 unmodified lines
180185 ::
181186
182187 10: hash name some_map flags 0x0
183− key 4B value 8B max_entries 2048 memlock 167936B
188+ key 4B value 8B max_entries 2048 memlock 167936B
189+ pids systemd(1)
184190
185191 The following three commands are equivalent:
186192
tools/bpf/bpftool/Documentation/bpftool-prog.rst+11−0
@@ -75,6 +75,11 @@ DESCRIPTION · 74 unmodified lines
7575 program run. Activation or deactivation of the feature is
7676 performed via the **kernel.bpf_stats_enabled** sysctl knob.
7777
78+ Since Linux 5.8 bpftool is able to discover information about
79+ processes that hold open file descriptors (FDs) against BPF
80+ programs. On such kernels bpftool will automatically emit this
81+ information as well.
82+
7883 **bpftool prog dump xlated** *PROG* [{ **file** *FILE* | **opcodes** | **visual** | **linum** }]
7984 Dump eBPF instructions of the programs from the kernel. By
8085 default, eBPF will be disassembled and printed to standard
@@ -243,6 +248,7 @@ EXAMPLES · 162 unmodified lines
243248 10: xdp name some_prog tag 005a3d2123620c8b gpl run_time_ns 81632 run_cnt 10
244249 loaded_at 2017-09-29T20:11:00+0000 uid 0
245250 xlated 528B jited 370B memlock 4096B map_ids 10
251+ pids systemd(1)
246252
247253 **# bpftool --json --pretty prog show**
248254
@@ -262,6 +268,11 @@ EXAMPLES · 13 unmodified lines
262268 "bytes_jited": 370,
263269 "bytes_memlock": 4096,
264270 "map_ids": [10
271+ ],
272+ "pids": [{
273+ "pid": 1,
274+ "comm": "systemd"
275+ }
265276 ]
266277 }
267278 ]
tools/bpf/bpftool/Makefile+39−21
@@ -42,6 +42,7 @@ CFLAGS += -O2 · 41 unmodified lines
4242 CFLAGS += -W -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers
4343 CFLAGS += $(filter-out -Wswitch-enum,$(EXTRA_WARNINGS))
4444 CFLAGS += -DPACKAGE='"bpftool"' -D__EXPORTED_HEADERS__ \
45+ -I$(if $(OUTPUT),$(OUTPUT),.) \
4546 -I$(srctree)/kernel/bpf/ \
4647 -I$(srctree)/tools/include \
4748 -I$(srctree)/tools/include/uapi \
@@ -61,9 +62,9 @@ CLANG ?= clang · 13 unmodified lines
6162
6263 FEATURE_USER = .bpftool
6364 FEATURE_TESTS = libbfd disassembler-four-args reallocarray zlib libcap \
64− clang-bpf-global-var
65+ clang-bpf-co-re
6566 FEATURE_DISPLAY = libbfd disassembler-four-args zlib libcap \
66− clang-bpf-global-var
67+ clang-bpf-co-re
6768
6869 check_feat := 1
6970 NON_CHECK_FEAT_TARGETS := clean uninstall doc doc-clean doc-install doc-uninstall
@@ -116,40 +117,56 @@ CFLAGS += -DHAVE_LIBBFD_SUPPORT · 46 unmodified lines
116117 SRCS += $(BFD_SRCS)
117118 endif
118119
120+BPFTOOL_BOOTSTRAP := $(if $(OUTPUT),$(OUTPUT)bpftool-bootstrap,./bpftool-bootstrap)
121+
122+BOOTSTRAP_OBJS = $(addprefix $(OUTPUT),main.o common.o json_writer.o gen.o btf.o)
119123 OBJS = $(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o
120−_OBJS = $(filter-out $(OUTPUT)prog.o,$(OBJS)) $(OUTPUT)_prog.o
121124
122−ifeq ($(feature-clang-bpf-global-var),1)
123− __OBJS = $(OBJS)
124−else
125− __OBJS = $(_OBJS)
126−endif
125+VMLINUX_BTF_PATHS := $(if $(O),$(O)/vmlinux) \
126+ $(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux) \
127+ ../../../vmlinux \
128+ /sys/kernel/btf/vmlinux \
129+ /boot/vmlinux-$(shell uname -r)
130+VMLINUX_BTF := $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS))))
127131
128−$(OUTPUT)_prog.o: prog.c
129− $(QUIET_CC)$(CC) $(CFLAGS) -c -MMD -DBPFTOOL_WITHOUT_SKELETONS -o $@ $<
132+ifneq ($(VMLINUX_BTF),)
133+ifeq ($(feature-clang-bpf-co-re),1)
130134
131−$(OUTPUT)_bpftool: $(_OBJS) $(LIBBPF)
132− $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(_OBJS) $(LIBS)
135+BUILD_BPF_SKELS := 1
133136
134−skeleton/profiler.bpf.o: skeleton/profiler.bpf.c $(LIBBPF)
137+$(OUTPUT)vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL_BOOTSTRAP)
138+ $(QUIET_GEN)$(BPFTOOL_BOOTSTRAP) btf dump file $< format c > $@
139+
140+$(OUTPUT)%.bpf.o: skeleton/%.bpf.c $(OUTPUT)vmlinux.h $(LIBBPF)
135141 $(QUIET_CLANG)$(CLANG) \
142+ -I$(if $(OUTPUT),$(OUTPUT),.) \
136143 -I$(srctree)/tools/include/uapi/ \
137− -I$(LIBBPF_PATH) -I$(srctree)/tools/lib \
144+ -I$(LIBBPF_PATH) \
145+ -I$(srctree)/tools/lib \
138146 -g -O2 -target bpf -c $< -o $@
139147
140−profiler.skel.h: $(OUTPUT)_bpftool skeleton/profiler.bpf.o
141− $(QUIET_GEN)$(OUTPUT)./_bpftool gen skeleton skeleton/profiler.bpf.o > $@
148+$(OUTPUT)%.skel.h: $(OUTPUT)%.bpf.o $(BPFTOOL_BOOTSTRAP)
149+ $(QUIET_GEN)$(BPFTOOL_BOOTSTRAP) gen skeleton $< > $@
142150
143−$(OUTPUT)prog.o: prog.c profiler.skel.h
144− $(QUIET_CC)$(CC) $(CFLAGS) -c -MMD -o $@ $<
151+$(OUTPUT)prog.o: $(OUTPUT)profiler.skel.h
152+
153+$(OUTPUT)pids.o: $(OUTPUT)pid_iter.skel.h
154+
155+endif
156+endif
157+
158+CFLAGS += $(if BUILD_BPF_SKELS,,-DBPFTOOL_WITHOUT_SKELETONS)
145159
146160 $(OUTPUT)disasm.o: $(srctree)/kernel/bpf/disasm.c
147161 $(QUIET_CC)$(CC) $(CFLAGS) -c -MMD -o $@ $<
148162
149163 $(OUTPUT)feature.o: | zdep
150164
151−$(OUTPUT)bpftool: $(__OBJS) $(LIBBPF)
152− $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(__OBJS) $(LIBS)
165+$(BPFTOOL_BOOTSTRAP): $(BOOTSTRAP_OBJS) $(LIBBPF)
166+ $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(BOOTSTRAP_OBJS) $(LIBS)
167+
168+$(OUTPUT)bpftool: $(OBJS) $(LIBBPF)
169+ $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
153170
154171 $(OUTPUT)%.o: %.c
155172 $(QUIET_CC)$(CC) $(CFLAGS) -c -MMD -o $@ $<
@@ -157,7 +174,7 @@ $(OUTPUT)%.o: %.c · 1 unmodified line
157174 clean: $(LIBBPF)-clean
158175 $(call QUIET_CLEAN, bpftool)
159176 $(Q)$(RM) -- $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d
160− $(Q)$(RM) -- $(OUTPUT)_bpftool profiler.skel.h skeleton/profiler.bpf.o
177+ $(Q)$(RM) -- $(BPFTOOL_BOOTSTRAP) $(OUTPUT)*.skel.h $(OUTPUT)vmlinux.h
161178 $(Q)$(RM) -r -- $(OUTPUT)libbpf/
162179 $(call QUIET_CLEAN, core-gen)
163180 $(Q)$(RM) -- $(OUTPUT)FEATURE-DUMP.bpftool
@@ -192,6 +209,7 @@ FORCE: · 28 unmodified lines
192209 zdep:
193210 @if [ "$(feature-zlib)" != "1" ]; then echo "No zlib found"; exit 1 ; fi
194211
212+.SECONDARY:
195213 .PHONY: all FORCE clean install uninstall zdep
196214 .PHONY: doc doc-clean doc-install doc-uninstall
197215 .DEFAULT_GOAL := all
tools/bpf/bpftool/btf.c+6−0
@@ -809,6 +809,7 @@ show_btf_plain(struct bpf_btf_info *info, int fd, · 808 unmodified lines
809809 printf("%s%u", n++ == 0 ? " map_ids " : ",",
810810 obj->obj_id);
811811 }
812+ emit_obj_refs_plain(&refs_table, info->id, "\n\tpids ");
812813
813814 printf("\n");
814815 }
@@ -841,6 +842,9 @@ show_btf_json(struct bpf_btf_info *info, int fd, · 26 unmodified lines
841842 jsonw_uint(json_wtr, obj->obj_id);
842843 }
843844 jsonw_end_array(json_wtr); /* map_ids */
845+
846+ emit_obj_refs_json(&refs_table, info->id, json_wtr); /* pids */
847+
844848 jsonw_end_object(json_wtr); /* btf object */
845849 }
846850
@@ -893,6 +897,7 @@ static int do_show(int argc, char **argv) · 46 unmodified lines
893897 close(fd);
894898 return err;
895899 }
900+ build_obj_refs_table(&refs_table, BPF_OBJ_BTF);
896901
897902 if (fd >= 0) {
898903 err = show_btf(fd, &btf_prog_table, &btf_map_table);
@@ -939,6 +944,7 @@ static int do_show(int argc, char **argv) · 40 unmodified lines
939944 exit_free:
940945 delete_btf_table(&btf_prog_table);
941946 delete_btf_table(&btf_map_table);
947+ delete_obj_refs_table(&refs_table);
942948
943949 return err;
944950 }
tools/bpf/bpftool/common.c+308−0
@@ -581,3 +581,311 @@ print_all_levels(__maybe_unused enum libbpf_print_level level, · 580 unmodified lines
581581 {
582582 return vfprintf(stderr, format, args);
583583 }
584+
585+static int prog_fd_by_nametag(void *nametag, int **fds, bool tag)
586+{
587+ unsigned int id = 0;
588+ int fd, nb_fds = 0;
589+ void *tmp;
590+ int err;
591+
592+ while (true) {
593+ struct bpf_prog_info info = {};
594+ __u32 len = sizeof(info);
595+
596+ err = bpf_prog_get_next_id(id, &id);
597+ if (err) {
598+ if (errno != ENOENT) {
599+ p_err("%s", strerror(errno));
600+ goto err_close_fds;
601+ }
602+ return nb_fds;
603+ }
604+
605+ fd = bpf_prog_get_fd_by_id(id);
606+ if (fd < 0) {
607+ p_err("can't get prog by id (%u): %s",
608+ id, strerror(errno));
609+ goto err_close_fds;
610+ }
611+
612+ err = bpf_obj_get_info_by_fd(fd, &info, &len);
613+ if (err) {
614+ p_err("can't get prog info (%u): %s",
615+ id, strerror(errno));
616+ goto err_close_fd;
617+ }
618+
619+ if ((tag && memcmp(nametag, info.tag, BPF_TAG_SIZE)) ||
620+ (!tag && strncmp(nametag, info.name, BPF_OBJ_NAME_LEN))) {
621+ close(fd);
622+ continue;
623+ }
624+
625+ if (nb_fds > 0) {
626+ tmp = realloc(*fds, (nb_fds + 1) * sizeof(int));
627+ if (!tmp) {
628+ p_err("failed to realloc");
629+ goto err_close_fd;
630+ }
631+ *fds = tmp;
632+ }
633+ (*fds)[nb_fds++] = fd;
634+ }
635+
636+err_close_fd:
637+ close(fd);
638+err_close_fds:
639+ while (--nb_fds >= 0)
640+ close((*fds)[nb_fds]);
641+ return -1;
642+}
643+
644+int prog_parse_fds(int *argc, char ***argv, int **fds)
645+{
646+ if (is_prefix(**argv, "id")) {
647+ unsigned int id;
648+ char *endptr;
649+
650+ NEXT_ARGP();
651+
652+ id = strtoul(**argv, &endptr, 0);
653+ if (*endptr) {
654+ p_err("can't parse %s as ID", **argv);
655+ return -1;
656+ }
657+ NEXT_ARGP();
658+
659+ (*fds)[0] = bpf_prog_get_fd_by_id(id);
660+ if ((*fds)[0] < 0) {
661+ p_err("get by id (%u): %s", id, strerror(errno));
662+ return -1;
663+ }
664+ return 1;
665+ } else if (is_prefix(**argv, "tag")) {
666+ unsigned char tag[BPF_TAG_SIZE];
667+
668+ NEXT_ARGP();
669+
670+ if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2,
671+ tag + 3, tag + 4, tag + 5, tag + 6, tag + 7)
672+ != BPF_TAG_SIZE) {
673+ p_err("can't parse tag");
674+ return -1;
675+ }
676+ NEXT_ARGP();
677+
678+ return prog_fd_by_nametag(tag, fds, true);
679+ } else if (is_prefix(**argv, "name")) {
680+ char *name;
681+
682+ NEXT_ARGP();
683+
684+ name = **argv;
685+ if (strlen(name) > BPF_OBJ_NAME_LEN - 1) {
686+ p_err("can't parse name");
687+ return -1;
688+ }
689+ NEXT_ARGP();
690+
691+ return prog_fd_by_nametag(name, fds, false);
692+ } else if (is_prefix(**argv, "pinned")) {
693+ char *path;
694+
695+ NEXT_ARGP();
696+
697+ path = **argv;
698+ NEXT_ARGP();
699+
700+ (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_PROG);
701+ if ((*fds)[0] < 0)
702+ return -1;
703+ return 1;
704+ }
705+
706+ p_err("expected 'id', 'tag', 'name' or 'pinned', got: '%s'?", **argv);
707+ return -1;
708+}
709+
710+int prog_parse_fd(int *argc, char ***argv)
711+{
712+ int *fds = NULL;
713+ int nb_fds, fd;
714+
715+ fds = malloc(sizeof(int));
716+ if (!fds) {
717+ p_err("mem alloc failed");
718+ return -1;
719+ }
720+ nb_fds = prog_parse_fds(argc, argv, &fds);
721+ if (nb_fds != 1) {
722+ if (nb_fds > 1) {
723+ p_err("several programs match this handle");
724+ while (nb_fds--)
725+ close(fds[nb_fds]);
726+ }
727+ fd = -1;
728+ goto exit_free;
729+ }
730+
731+ fd = fds[0];
732+exit_free:
733+ free(fds);
734+ return fd;
735+}
736+
737+static int map_fd_by_name(char *name, int **fds)
738+{
739+ unsigned int id = 0;
740+ int fd, nb_fds = 0;
741+ void *tmp;
742+ int err;
743+
744+ while (true) {
745+ struct bpf_map_info info = {};
746+ __u32 len = sizeof(info);
747+
748+ err = bpf_map_get_next_id(id, &id);
749+ if (err) {
750+ if (errno != ENOENT) {
751+ p_err("%s", strerror(errno));
752+ goto err_close_fds;
753+ }
754+ return nb_fds;
755+ }
756+
757+ fd = bpf_map_get_fd_by_id(id);
758+ if (fd < 0) {
759+ p_err("can't get map by id (%u): %s",
760+ id, strerror(errno));
761+ goto err_close_fds;
762+ }
763+
764+ err = bpf_obj_get_info_by_fd(fd, &info, &len);
765+ if (err) {
766+ p_err("can't get map info (%u): %s",
767+ id, strerror(errno));
768+ goto err_close_fd;
769+ }
770+
771+ if (strncmp(name, info.name, BPF_OBJ_NAME_LEN)) {
772+ close(fd);
773+ continue;
774+ }
775+
776+ if (nb_fds > 0) {
777+ tmp = realloc(*fds, (nb_fds + 1) * sizeof(int));
778+ if (!tmp) {
779+ p_err("failed to realloc");
780+ goto err_close_fd;
781+ }
782+ *fds = tmp;
783+ }
784+ (*fds)[nb_fds++] = fd;
785+ }
786+
787+err_close_fd:
788+ close(fd);
789+err_close_fds:
790+ while (--nb_fds >= 0)
791+ close((*fds)[nb_fds]);
792+ return -1;
793+}
794+
795+int map_parse_fds(int *argc, char ***argv, int **fds)
796+{
797+ if (is_prefix(**argv, "id")) {
798+ unsigned int id;
799+ char *endptr;
800+
801+ NEXT_ARGP();
802+
803+ id = strtoul(**argv, &endptr, 0);
804+ if (*endptr) {
805+ p_err("can't parse %s as ID", **argv);
806+ return -1;
807+ }
808+ NEXT_ARGP();
809+
810+ (*fds)[0] = bpf_map_get_fd_by_id(id);
811+ if ((*fds)[0] < 0) {
812+ p_err("get map by id (%u): %s", id, strerror(errno));
813+ return -1;
814+ }
815+ return 1;
816+ } else if (is_prefix(**argv, "name")) {
817+ char *name;
818+
819+ NEXT_ARGP();
820+
821+ name = **argv;
822+ if (strlen(name) > BPF_OBJ_NAME_LEN - 1) {
823+ p_err("can't parse name");
824+ return -1;
825+ }
826+ NEXT_ARGP();
827+
828+ return map_fd_by_name(name, fds);
829+ } else if (is_prefix(**argv, "pinned")) {
830+ char *path;
831+
832+ NEXT_ARGP();
833+
834+ path = **argv;
835+ NEXT_ARGP();
836+
837+ (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_MAP);
838+ if ((*fds)[0] < 0)
839+ return -1;
840+ return 1;
841+ }
842+
843+ p_err("expected 'id', 'name' or 'pinned', got: '%s'?", **argv);
844+ return -1;
845+}
846+
847+int map_parse_fd(int *argc, char ***argv)
848+{
849+ int *fds = NULL;
850+ int nb_fds, fd;
851+
852+ fds = malloc(sizeof(int));
853+ if (!fds) {
854+ p_err("mem alloc failed");
855+ return -1;
856+ }
857+ nb_fds = map_parse_fds(argc, argv, &fds);
858+ if (nb_fds != 1) {
859+ if (nb_fds > 1) {
860+ p_err("several maps match this handle");
861+ while (nb_fds--)
862+ close(fds[nb_fds]);
863+ }
864+ fd = -1;
865+ goto exit_free;
866+ }
867+
868+ fd = fds[0];
869+exit_free:
870+ free(fds);
871+ return fd;
872+}
873+
874+int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len)
875+{
876+ int err;
877+ int fd;
878+
879+ fd = map_parse_fd(argc, argv);
880+ if (fd < 0)
881+ return -1;
882+
883+ err = bpf_obj_get_info_by_fd(fd, info, info_len);
884+ if (err) {
885+ p_err("can't get map info: %s", strerror(errno));
886+ close(fd);
887+ return err;
888+ }
889+
890+ return fd;
891+}
tools/bpf/bpftool/link.c+7−0
@@ -143,6 +143,9 @@ static int show_link_close_json(int fd, struct bpf_link_info *info) · 142 unmodified lines
143143 }
144144 jsonw_end_array(json_wtr);
145145 }
146+
147+ emit_obj_refs_json(&refs_table, info->id, json_wtr);
148+
146149 jsonw_end_object(json_wtr);
147150
148151 return 0;
@@ -212,6 +215,7 @@ static int show_link_close_plain(int fd, struct bpf_link_info *info) · 63 unmodified lines
212215 printf("\n\tpinned %s", obj->path);
213216 }
214217 }
218+ emit_obj_refs_plain(&refs_table, info->id, "\n\tpids ");
215219
216220 printf("\n");
217221
@@ -257,6 +261,7 @@ static int do_show(int argc, char **argv) · 39 unmodified lines
257261
258262 if (show_pinned)
259263 build_pinned_obj_table(&link_table, BPF_OBJ_LINK);
264+ build_obj_refs_table(&refs_table, BPF_OBJ_LINK);
260265
261266 if (argc == 2) {
262267 fd = link_parse_fd(&argc, &argv);
@@ -296,6 +301,8 @@ static int do_show(int argc, char **argv) · 33 unmodified lines
296301 if (json_output)
297302 jsonw_end_array(json_wtr);
298303
304+ delete_obj_refs_table(&refs_table);
305+
299306 return errno == ENOENT ? 0 : -1;
300307 }
301308
tools/bpf/bpftool/main.c+10−2
@@ -31,6 +31,7 @@ bool relaxed_maps; · 30 unmodified lines
3131 struct pinned_obj_table prog_table;
3232 struct pinned_obj_table map_table;
3333 struct pinned_obj_table link_table;
34+struct obj_refs_table refs_table;
3435
3536 static void __noreturn clean_and_exit(int i)
3637 {
@@ -92,9 +93,16 @@ int cmd_select(const struct cmd *cmds, int argc, char **argv, · 55 unmodified lines
9293 if (argc < 1 && cmds[0].func)
9394 return cmds[0].func(argc, argv);
9495
95− for (i = 0; cmds[i].func; i++)
96− if (is_prefix(*argv, cmds[i].cmd))
96+ for (i = 0; cmds[i].cmd; i++) {
97+ if (is_prefix(*argv, cmds[i].cmd)) {
98+ if (!cmds[i].func) {
99+ p_err("command '%s' is not supported in bootstrap mode",
100+ cmds[i].cmd);
101+ return -1;
102+ }
97103 return cmds[i].func(argc - 1, argv + 1);
104+ }
105+ }
98106
99107 help(argc - 1, argv + 1);
100108
tools/bpf/bpftool/main.h+44−12
@@ -127,11 +127,13 @@ static const char * const attach_type_name[__MAX_BPF_ATTACH_TYPE] = { · 126 unmodified lines
127127 extern const char * const map_type_name[];
128128 extern const size_t map_type_name_size;
129129
130+/* keep in sync with the definition in skeleton/pid_iter.bpf.c */
130131 enum bpf_obj_type {
131132 BPF_OBJ_UNKNOWN,
132133 BPF_OBJ_PROG,
133134 BPF_OBJ_MAP,
134135 BPF_OBJ_LINK,
136+ BPF_OBJ_BTF,
135137 };
136138
137139 extern const char *bin_name;
@@ -139,12 +141,14 @@ extern const char *bin_name; · 1 unmodified line
139141 extern json_writer_t *json_wtr;
140142 extern bool json_output;
141143 extern bool show_pinned;
144+extern bool show_pids;
142145 extern bool block_mount;
143146 extern bool verifier_logs;
144147 extern bool relaxed_maps;
145148 extern struct pinned_obj_table prog_table;
146149 extern struct pinned_obj_table map_table;
147150 extern struct pinned_obj_table link_table;
151+extern struct obj_refs_table refs_table;
148152
149153 void __printf(1, 2) p_err(const char *fmt, ...);
150154 void __printf(1, 2) p_info(const char *fmt, ...);
@@ -168,12 +172,35 @@ struct pinned_obj { · 17 unmodified lines
168172 struct hlist_node hash;
169173 };
170174
175+struct obj_refs_table {
176+ DECLARE_HASHTABLE(table, 16);
177+};
178+
179+struct obj_ref {
180+ int pid;
181+ char comm[16];
182+};
183+
184+struct obj_refs {
185+ struct hlist_node node;
186+ __u32 id;
187+ int ref_cnt;
188+ struct obj_ref *refs;
189+};
190+
171191 struct btf;
172192 struct bpf_line_info;
173193
174194 int build_pinned_obj_table(struct pinned_obj_table *table,
175195 enum bpf_obj_type type);
176196 void delete_pinned_obj_table(struct pinned_obj_table *tab);
197+__weak int build_obj_refs_table(struct obj_refs_table *table,
198+ enum bpf_obj_type type);
199+__weak void delete_obj_refs_table(struct obj_refs_table *table);
200+__weak void emit_obj_refs_json(struct obj_refs_table *table, __u32 id,
201+ json_writer_t *json_wtr);
202+__weak void emit_obj_refs_plain(struct obj_refs_table *table, __u32 id,
203+ const char *prefix);
177204 void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode);
178205 void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode);
179206
@@ -194,23 +221,28 @@ int mount_bpffs_for_pin(const char *name); · 14 unmodified lines
194221 int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(int *, char ***));
195222 int do_pin_fd(int fd, const char *name);
196223
197−int do_prog(int argc, char **arg);
198−int do_map(int argc, char **arg);
199−int do_link(int argc, char **arg);
200−int do_event_pipe(int argc, char **argv);
201−int do_cgroup(int argc, char **arg);
202−int do_perf(int argc, char **arg);
203−int do_net(int argc, char **arg);
204−int do_tracelog(int argc, char **arg);
205−int do_feature(int argc, char **argv);
206−int do_btf(int argc, char **argv);
224+/* commands available in bootstrap mode */
207225 int do_gen(int argc, char **argv);
208−int do_struct_ops(int argc, char **argv);
209−int do_iter(int argc, char **argv);
226+int do_btf(int argc, char **argv);
227+
228+/* non-bootstrap only commands */
229+int do_prog(int argc, char **arg) __weak;
230+int do_map(int argc, char **arg) __weak;
231+int do_link(int argc, char **arg) __weak;
232+int do_event_pipe(int argc, char **argv) __weak;
233+int do_cgroup(int argc, char **arg) __weak;
234+int do_perf(int argc, char **arg) __weak;
235+int do_net(int argc, char **arg) __weak;
236+int do_tracelog(int argc, char **arg) __weak;
237+int do_feature(int argc, char **argv) __weak;
238+int do_struct_ops(int argc, char **argv) __weak;
239+int do_iter(int argc, char **argv) __weak;
210240
211241 int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what);
212242 int prog_parse_fd(int *argc, char ***argv);
243+int prog_parse_fds(int *argc, char ***argv, int **fds);
213244 int map_parse_fd(int *argc, char ***argv);
245+int map_parse_fds(int *argc, char ***argv, int **fds);
214246 int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len);
215247
216248 struct bpf_prog_linfo;
tools/bpf/bpftool/map.c+7−156
@@ -92,162 +92,6 @@ static void *alloc_value(struct bpf_map_info *info) · 91 unmodified lines
9292 return malloc(info->value_size);
9393 }
9494
95−static int map_fd_by_name(char *name, int **fds)
96−{
97− unsigned int id = 0;
98− int fd, nb_fds = 0;
99− void *tmp;
100− int err;
101−
102− while (true) {
103− struct bpf_map_info info = {};
104− __u32 len = sizeof(info);
105−
106− err = bpf_map_get_next_id(id, &id);
107− if (err) {
108− if (errno != ENOENT) {
109− p_err("%s", strerror(errno));
110− goto err_close_fds;
111− }
112− return nb_fds;
113− }
114−
115− fd = bpf_map_get_fd_by_id(id);
116− if (fd < 0) {
117− p_err("can't get map by id (%u): %s",
118− id, strerror(errno));
119− goto err_close_fds;
120− }
121−
122− err = bpf_obj_get_info_by_fd(fd, &info, &len);
123− if (err) {
124− p_err("can't get map info (%u): %s",
125− id, strerror(errno));
126− goto err_close_fd;
127− }
128−
129− if (strncmp(name, info.name, BPF_OBJ_NAME_LEN)) {
130− close(fd);
131− continue;
132− }
133−
134− if (nb_fds > 0) {
135− tmp = realloc(*fds, (nb_fds + 1) * sizeof(int));
136− if (!tmp) {
137− p_err("failed to realloc");
138− goto err_close_fd;
139− }
140− *fds = tmp;
141− }
142− (*fds)[nb_fds++] = fd;
143− }
144−
145−err_close_fd:
146− close(fd);
147−err_close_fds:
148− while (--nb_fds >= 0)
149− close((*fds)[nb_fds]);
150− return -1;
151−}
152−
153−static int map_parse_fds(int *argc, char ***argv, int **fds)
154−{
155− if (is_prefix(**argv, "id")) {
156− unsigned int id;
157− char *endptr;
158−
159− NEXT_ARGP();
160−
161− id = strtoul(**argv, &endptr, 0);
162− if (*endptr) {
163− p_err("can't parse %s as ID", **argv);
164− return -1;
165− }
166− NEXT_ARGP();
167−
168− (*fds)[0] = bpf_map_get_fd_by_id(id);
169− if ((*fds)[0] < 0) {
170− p_err("get map by id (%u): %s", id, strerror(errno));
171− return -1;
172− }
173− return 1;
174− } else if (is_prefix(**argv, "name")) {
175− char *name;
176−
177− NEXT_ARGP();
178−
179− name = **argv;
180− if (strlen(name) > BPF_OBJ_NAME_LEN - 1) {
181− p_err("can't parse name");
182− return -1;
183− }
184− NEXT_ARGP();
185−
186− return map_fd_by_name(name, fds);
187− } else if (is_prefix(**argv, "pinned")) {
188− char *path;
189−
190− NEXT_ARGP();
191−
192− path = **argv;
193− NEXT_ARGP();
194−
195− (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_MAP);
196− if ((*fds)[0] < 0)
197− return -1;
198− return 1;
199− }
200−
201− p_err("expected 'id', 'name' or 'pinned', got: '%s'?", **argv);
202− return -1;
203−}
204−
205−int map_parse_fd(int *argc, char ***argv)
206−{
207− int *fds = NULL;
208− int nb_fds, fd;
209−
210− fds = malloc(sizeof(int));
211− if (!fds) {
212− p_err("mem alloc failed");
213− return -1;
214− }
215− nb_fds = map_parse_fds(argc, argv, &fds);
216− if (nb_fds != 1) {
217− if (nb_fds > 1) {
218− p_err("several maps match this handle");
219− while (nb_fds--)
220− close(fds[nb_fds]);
221− }
222− fd = -1;
223− goto exit_free;
224− }
225−
226− fd = fds[0];
227−exit_free:
228− free(fds);
229− return fd;
230−}
231−
232−int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len)
233−{
234− int err;
235− int fd;
236−
237− fd = map_parse_fd(argc, argv);
238− if (fd < 0)
239− return -1;
240−
241− err = bpf_obj_get_info_by_fd(fd, info, info_len);
242− if (err) {
243− p_err("can't get map info: %s", strerror(errno));
244− close(fd);
245− return err;
246− }
247−
248− return fd;
249−}
250−
25195 static int do_dump_btf(const struct btf_dumper *d,
25296 struct bpf_map_info *map_info, void *key,
25397 void *value)
@@ -665,6 +509,8 @@ static int show_map_close_json(int fd, struct bpf_map_info *info) · 411 unmodified lines
665509 jsonw_end_array(json_wtr);
666510 }
667511
512+ emit_obj_refs_json(&refs_table, info->id, json_wtr);
513+
668514 jsonw_end_object(json_wtr);
669515
670516 return 0;
@@ -752,6 +598,8 @@ static int show_map_close_plain(int fd, struct bpf_map_info *info) · 81 unmodified lines
752598 if (frozen)
753599 printf("%sfrozen", info->btf_id ? " " : "");
754600
601+ emit_obj_refs_plain(&refs_table, info->id, "\n\tpids ");
602+
755603 printf("\n");
756604 return 0;
757605 }
@@ -810,6 +658,7 @@ static int do_show(int argc, char **argv) · 52 unmodified lines
810658
811659 if (show_pinned)
812660 build_pinned_obj_table(&map_table, BPF_OBJ_MAP);
661+ build_obj_refs_table(&refs_table, BPF_OBJ_MAP);
813662
814663 if (argc == 2)
815664 return do_show_subset(argc, argv);
@@ -853,6 +702,8 @@ static int do_show(int argc, char **argv) · 37 unmodified lines
853702 if (json_output)
854703 jsonw_end_array(json_wtr);
855704
705+ delete_obj_refs_table(&refs_table);
706+
856707 return errno == ENOENT ? 0 : -1;
857708 }
858709
tools/bpf/bpftool/pids.c+229−0
@@ -0,0 +1,229 @@
1+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2+/* Copyright (C) 2020 Facebook */
3+#include <errno.h>
4+#include <stdbool.h>
5+#include <stdio.h>
6+#include <stdlib.h>
7+#include <string.h>
8+#include <unistd.h>
9+#include <bpf/bpf.h>
10+
11+#include "main.h"
12+#include "skeleton/pid_iter.h"
13+
14+#ifdef BPFTOOL_WITHOUT_SKELETONS
15+
16+int build_obj_refs_table(struct obj_refs_table *table, enum bpf_obj_type type)
17+{
18+ p_err("bpftool built without PID iterator support");
19+ return -ENOTSUP;
20+}
21+void delete_obj_refs_table(struct obj_refs_table *table) {}
22+
23+#else /* BPFTOOL_WITHOUT_SKELETONS */
24+
25+#include "pid_iter.skel.h"
26+
27+static void add_ref(struct obj_refs_table *table, struct pid_iter_entry *e)
28+{
29+ struct obj_refs *refs;
30+ struct obj_ref *ref;
31+ void *tmp;
32+ int i;
33+
34+ hash_for_each_possible(table->table, refs, node, e->id) {
35+ if (refs->id != e->id)
36+ continue;
37+
38+ for (i = 0; i < refs->ref_cnt; i++) {
39+ if (refs->refs[i].pid == e->pid)
40+ return;
41+ }
42+
43+ tmp = realloc(refs->refs, (refs->ref_cnt + 1) * sizeof(*ref));
44+ if (!tmp) {
45+ p_err("failed to re-alloc memory for ID %u, PID %d, COMM %s...",
46+ e->id, e->pid, e->comm);
47+ return;
48+ }
49+ refs->refs = tmp;
50+ ref = &refs->refs[refs->ref_cnt];
51+ ref->pid = e->pid;
52+ memcpy(ref->comm, e->comm, sizeof(ref->comm));
53+ refs->ref_cnt++;
54+
55+ return;
56+ }
57+
58+ /* new ref */
59+ refs = calloc(1, sizeof(*refs));
60+ if (!refs) {
61+ p_err("failed to alloc memory for ID %u, PID %d, COMM %s...",
62+ e->id, e->pid, e->comm);
63+ return;
64+ }
65+
66+ refs->id = e->id;
67+ refs->refs = malloc(sizeof(*refs->refs));
68+ if (!refs->refs) {
69+ free(refs);
70+ p_err("failed to alloc memory for ID %u, PID %d, COMM %s...",
71+ e->id, e->pid, e->comm);
72+ return;
73+ }
74+ ref = &refs->refs[0];
75+ ref->pid = e->pid;
76+ memcpy(ref->comm, e->comm, sizeof(ref->comm));
77+ refs->ref_cnt = 1;
78+ hash_add(table->table, &refs->node, e->id);
79+}
80+
81+static int __printf(2, 0)
82+libbpf_print_none(__maybe_unused enum libbpf_print_level level,
83+ __maybe_unused const char *format,
84+ __maybe_unused va_list args)
85+{
86+ return 0;
87+}
88+
89+int build_obj_refs_table(struct obj_refs_table *table, enum bpf_obj_type type)
90+{
91+ char buf[4096];
92+ struct pid_iter_bpf *skel;
93+ struct pid_iter_entry *e;
94+ int err, ret, fd = -1, i;
95+ libbpf_print_fn_t default_print;
96+
97+ hash_init(table->table);
98+ set_max_rlimit();
99+
100+ skel = pid_iter_bpf__open();
101+ if (!skel) {
102+ p_err("failed to open PID iterator skeleton");
103+ return -1;
104+ }
105+
106+ skel->rodata->obj_type = type;
107+
108+ /* we don't want output polluted with libbpf errors if bpf_iter is not
109+ * supported
110+ */
111+ default_print = libbpf_set_print(libbpf_print_none);
112+ err = pid_iter_bpf__load(skel);
113+ libbpf_set_print(default_print);
114+ if (err) {
115+ /* too bad, kernel doesn't support BPF iterators yet */
116+ err = 0;
117+ goto out;
118+ }
119+ err = pid_iter_bpf__attach(skel);
120+ if (err) {
121+ /* if we loaded above successfully, attach has to succeed */
122+ p_err("failed to attach PID iterator: %d", err);
123+ goto out;
124+ }
125+
126+ fd = bpf_iter_create(bpf_link__fd(skel->links.iter));
127+ if (fd < 0) {
128+ err = -errno;
129+ p_err("failed to create PID iterator session: %d", err);
130+ goto out;
131+ }
132+
133+ while (true) {
134+ ret = read(fd, buf, sizeof(buf));
135+ if (ret < 0) {
136+ err = -errno;
137+ p_err("failed to read PID iterator output: %d", err);
138+ goto out;
139+ }
140+ if (ret == 0)
141+ break;
142+ if (ret % sizeof(*e)) {
143+ err = -EINVAL;
144+ p_err("invalid PID iterator output format");
145+ goto out;
146+ }
147+ ret /= sizeof(*e);
148+
149+ e = (void *)buf;
150+ for (i = 0; i < ret; i++, e++) {
151+ add_ref(table, e);
152+ }
153+ }
154+ err = 0;
155+out:
156+ if (fd >= 0)
157+ close(fd);
158+ pid_iter_bpf__destroy(skel);
159+ return err;
160+}
161+
162+void delete_obj_refs_table(struct obj_refs_table *table)
163+{
164+ struct obj_refs *refs;
165+ struct hlist_node *tmp;
166+ unsigned int bkt;
167+
168+ hash_for_each_safe(table->table, bkt, tmp, refs, node) {
169+ hash_del(&refs->node);
170+ free(refs->refs);
171+ free(refs);
172+ }
173+}
174+
175+void emit_obj_refs_json(struct obj_refs_table *table, __u32 id, json_writer_t *json_wtr)
176+{
177+ struct obj_refs *refs;
178+ struct obj_ref *ref;
179+ int i;
180+
181+ if (hash_empty(table->table))
182+ return;
183+
184+ hash_for_each_possible(table->table, refs, node, id) {
185+ if (refs->id != id)
186+ continue;
187+ if (refs->ref_cnt == 0)
188+ break;
189+
190+ jsonw_name(json_wtr, "pids");
191+ jsonw_start_array(json_wtr);
192+ for (i = 0; i < refs->ref_cnt; i++) {
193+ ref = &refs->refs[i];
194+ jsonw_start_object(json_wtr);
195+ jsonw_int_field(json_wtr, "pid", ref->pid);
196+ jsonw_string_field(json_wtr, "comm", ref->comm);
197+ jsonw_end_object(json_wtr);
198+ }
199+ jsonw_end_array(json_wtr);
200+ break;
201+ }
202+}
203+
204+void emit_obj_refs_plain(struct obj_refs_table *table, __u32 id, const char *prefix)
205+{
206+ struct obj_refs *refs;
207+ struct obj_ref *ref;
208+ int i;
209+
210+ if (hash_empty(table->table))
211+ return;
212+
213+ hash_for_each_possible(table->table, refs, node, id) {
214+ if (refs->id != id)
215+ continue;
216+ if (refs->ref_cnt == 0)
217+ break;
218+
219+ printf("%s", prefix);
220+ for (i = 0; i < refs->ref_cnt; i++) {
221+ ref = &refs->refs[i];
222+ printf("%s%s(%d)", i == 0 ? "" : ", ", ref->comm, ref->pid);
223+ }
224+ break;
225+ }
226+}
227+
228+
229+#endif
tools/bpf/bpftool/prog.c+7−152
@@ -86,158 +86,6 @@ static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) · 85 unmodified lines
8686 strftime(buf, size, "%FT%T%z", &load_tm);
8787 }
8888
89−static int prog_fd_by_nametag(void *nametag, int **fds, bool tag)
90−{
91− unsigned int id = 0;
92− int fd, nb_fds = 0;
93− void *tmp;
94− int err;
95−
96− while (true) {
97− struct bpf_prog_info info = {};
98− __u32 len = sizeof(info);
99−
100− err = bpf_prog_get_next_id(id, &id);
101− if (err) {
102− if (errno != ENOENT) {
103− p_err("%s", strerror(errno));
104− goto err_close_fds;
105− }
106− return nb_fds;
107− }
108−
109− fd = bpf_prog_get_fd_by_id(id);
110− if (fd < 0) {
111− p_err("can't get prog by id (%u): %s",
112− id, strerror(errno));
113− goto err_close_fds;
114− }
115−
116− err = bpf_obj_get_info_by_fd(fd, &info, &len);
117− if (err) {
118− p_err("can't get prog info (%u): %s",
119− id, strerror(errno));
120− goto err_close_fd;
121− }
122−
123− if ((tag && memcmp(nametag, info.tag, BPF_TAG_SIZE)) ||
124− (!tag && strncmp(nametag, info.name, BPF_OBJ_NAME_LEN))) {
125− close(fd);
126− continue;
127− }
128−
129− if (nb_fds > 0) {
130− tmp = realloc(*fds, (nb_fds + 1) * sizeof(int));
131− if (!tmp) {
132− p_err("failed to realloc");
133− goto err_close_fd;
134− }
135− *fds = tmp;
136− }
137− (*fds)[nb_fds++] = fd;
138− }
139−
140−err_close_fd:
141− close(fd);
142−err_close_fds:
143− while (--nb_fds >= 0)
144− close((*fds)[nb_fds]);
145− return -1;
146−}
147−
148−static int prog_parse_fds(int *argc, char ***argv, int **fds)
149−{
150− if (is_prefix(**argv, "id")) {
151− unsigned int id;
152− char *endptr;
153−
154− NEXT_ARGP();
155−
156− id = strtoul(**argv, &endptr, 0);
157− if (*endptr) {
158− p_err("can't parse %s as ID", **argv);
159− return -1;
160− }
161− NEXT_ARGP();
162−
163− (*fds)[0] = bpf_prog_get_fd_by_id(id);
164− if ((*fds)[0] < 0) {
165− p_err("get by id (%u): %s", id, strerror(errno));
166− return -1;
167− }
168− return 1;
169− } else if (is_prefix(**argv, "tag")) {
170− unsigned char tag[BPF_TAG_SIZE];
171−
172− NEXT_ARGP();
173−
174− if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2,
175− tag + 3, tag + 4, tag + 5, tag + 6, tag + 7)
176− != BPF_TAG_SIZE) {
177− p_err("can't parse tag");
178− return -1;
179− }
180− NEXT_ARGP();
181−
182− return prog_fd_by_nametag(tag, fds, true);
183− } else if (is_prefix(**argv, "name")) {
184− char *name;
185−
186− NEXT_ARGP();
187−
188− name = **argv;
189− if (strlen(name) > BPF_OBJ_NAME_LEN - 1) {
190− p_err("can't parse name");
191− return -1;
192− }
193− NEXT_ARGP();
194−
195− return prog_fd_by_nametag(name, fds, false);
196− } else if (is_prefix(**argv, "pinned")) {
197− char *path;
198−
199− NEXT_ARGP();
200−
201− path = **argv;
202− NEXT_ARGP();
203−
204− (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_PROG);
205− if ((*fds)[0] < 0)
206− return -1;
207− return 1;
208− }
209−
210− p_err("expected 'id', 'tag', 'name' or 'pinned', got: '%s'?", **argv);
211− return -1;
212−}
213−
214−int prog_parse_fd(int *argc, char ***argv)
215−{
216− int *fds = NULL;
217− int nb_fds, fd;
218−
219− fds = malloc(sizeof(int));
220− if (!fds) {
221− p_err("mem alloc failed");
222− return -1;
223− }
224− nb_fds = prog_parse_fds(argc, argv, &fds);
225− if (nb_fds != 1) {
226− if (nb_fds > 1) {
227− p_err("several programs match this handle");
228− while (nb_fds--)
229− close(fds[nb_fds]);
230− }
231− fd = -1;
232− goto exit_free;
233− }
234−
235− fd = fds[0];
236−exit_free:
237− free(fds);
238− return fd;
239−}
240−
24189 static void show_prog_maps(int fd, __u32 num_maps)
24290 {
24391 struct bpf_prog_info info = {};
@@ -342,6 +190,8 @@ static void print_prog_json(struct bpf_prog_info *info, int fd) · 98 unmodified lines
342190 jsonw_end_array(json_wtr);
343191 }
344192
193+ emit_obj_refs_json(&refs_table, info->id, json_wtr);
194+
345195 jsonw_end_object(json_wtr);
346196 }
347197
@@ -408,6 +258,8 @@ static void print_prog_plain(struct bpf_prog_info *info, int fd) · 60 unmodified lines
408258 if (info->btf_id)
409259 printf("\n\tbtf_id %d", info->btf_id);
410260
261+ emit_obj_refs_plain(&refs_table, info->id, "\n\tpids ");
262+
411263 printf("\n");
412264 }
413265
@@ -473,6 +325,7 @@ static int do_show(int argc, char **argv) · 59 unmodified lines
473325
474326 if (show_pinned)
475327 build_pinned_obj_table(&prog_table, BPF_OBJ_PROG);
328+ build_obj_refs_table(&refs_table, BPF_OBJ_PROG);
476329
477330 if (argc == 2)
478331 return do_show_subset(argc, argv);
@@ -514,6 +367,8 @@ static int do_show(int argc, char **argv) · 35 unmodified lines
514367 if (json_output)
515368 jsonw_end_array(json_wtr);
516369
370+ delete_obj_refs_table(&refs_table);
371+
517372 return err;
518373 }
519374
tools/bpf/bpftool/skeleton/pid_iter.bpf.c+80−0
@@ -0,0 +1,80 @@
1+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2+/* Copyright (c) 2020 Facebook */
3+#include <vmlinux.h>
4+#include <bpf/bpf_helpers.h>
5+#include <bpf/bpf_core_read.h>
6+#include <bpf/bpf_tracing.h>
7+#include "pid_iter.h"
8+
9+/* keep in sync with the definition in main.h */
10+enum bpf_obj_type {
11+ BPF_OBJ_UNKNOWN,
12+ BPF_OBJ_PROG,
13+ BPF_OBJ_MAP,
14+ BPF_OBJ_LINK,
15+ BPF_OBJ_BTF,
16+};
17+
18+extern const void bpf_link_fops __ksym;
19+extern const void bpf_map_fops __ksym;
20+extern const void bpf_prog_fops __ksym;
21+extern const void btf_fops __ksym;
22+
23+const volatile enum bpf_obj_type obj_type = BPF_OBJ_UNKNOWN;
24+
25+static __always_inline __u32 get_obj_id(void *ent, enum bpf_obj_type type)
26+{
27+ switch (type) {
28+ case BPF_OBJ_PROG:
29+ return BPF_CORE_READ((struct bpf_prog *)ent, aux, id);
30+ case BPF_OBJ_MAP:
31+ return BPF_CORE_READ((struct bpf_map *)ent, id);
32+ case BPF_OBJ_BTF:
33+ return BPF_CORE_READ((struct btf *)ent, id);
34+ case BPF_OBJ_LINK:
35+ return BPF_CORE_READ((struct bpf_link *)ent, id);
36+ default:
37+ return 0;
38+ }
39+}
40+
41+SEC("iter/task_file")
42+int iter(struct bpf_iter__task_file *ctx)
43+{
44+ struct file *file = ctx->file;
45+ struct task_struct *task = ctx->task;
46+ struct pid_iter_entry e;
47+ const void *fops;
48+
49+ if (!file || !task)
50+ return 0;
51+
52+ switch (obj_type) {
53+ case BPF_OBJ_PROG:
54+ fops = &bpf_prog_fops;
55+ break;
56+ case BPF_OBJ_MAP:
57+ fops = &bpf_map_fops;
58+ break;
59+ case BPF_OBJ_BTF:
60+ fops = &btf_fops;
61+ break;
62+ case BPF_OBJ_LINK:
63+ fops = &bpf_link_fops;
64+ break;
65+ default:
66+ return 0;
67+ }
68+
69+ if (file->f_op != fops)
70+ return 0;
71+
72+ e.pid = task->tgid;
73+ e.id = get_obj_id(file->private_data, obj_type);
74+ bpf_probe_read(&e.comm, sizeof(e.comm), task->group_leader->comm);
75+ bpf_seq_write(ctx->meta->seq, &e, sizeof(e));
76+
77+ return 0;
78+}
79+
80+char LICENSE[] SEC("license") = "Dual BSD/GPL";
tools/bpf/bpftool/skeleton/pid_iter.h+12−0
@@ -0,0 +1,12 @@
1+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
2+/* Copyright (c) 2020 Facebook */
3+#ifndef __PID_ITER_H
4+#define __PID_ITER_H
5+
6+struct pid_iter_entry {
7+ __u32 id;
8+ int pid;
9+ char comm[16];
10+};
11+
12+#endif
tools/bpf/bpftool/skeleton/profiler.bpf.c+1−2
@@ -1,7 +1,6 @@
11 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
22 // Copyright (c) 2020 Facebook
3−#include "profiler.h"
4−#include <linux/bpf.h>
3+#include <vmlinux.h>
54 #include <bpf/bpf_helpers.h>
65 #include <bpf/bpf_tracing.h>
76
tools/bpf/bpftool/skeleton/profiler.h+0−46
@@ -1,46 +0,0 @@
1−/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
2−#ifndef __PROFILER_H
3−#define __PROFILER_H
4−
5−/* useful typedefs from vmlinux.h */
6−
7−typedef signed char __s8;
8−typedef unsigned char __u8;
9−typedef short int __s16;
10−typedef short unsigned int __u16;
11−typedef int __s32;
12−typedef unsigned int __u32;
13−typedef long long int __s64;
14−typedef long long unsigned int __u64;
15−
16−typedef __s8 s8;
17−typedef __u8 u8;
18−typedef __s16 s16;
19−typedef __u16 u16;
20−typedef __s32 s32;
21−typedef __u32 u32;
22−typedef __s64 s64;
23−typedef __u64 u64;
24−
25−enum {
26− false = 0,
27− true = 1,
28−};
29−
30−#ifdef __CHECKER__
31−#define __bitwise__ __attribute__((bitwise))
32−#else
33−#define __bitwise__
34−#endif
35−
36−typedef __u16 __bitwise__ __le16;
37−typedef __u16 __bitwise__ __be16;
38−typedef __u32 __bitwise__ __le32;
39−typedef __u32 __bitwise__ __be32;
40−typedef __u64 __bitwise__ __le64;
41−typedef __u64 __bitwise__ __be64;
42−
43−typedef __u16 __bitwise__ __sum16;
44−typedef __u32 __bitwise__ __wsum;
45−
46−#endif /* __PROFILER_H */
tools/build/feature/Makefile+2−2
@@ -68,7 +68,7 @@ FILES= \ · 67 unmodified lines
6868 test-llvm-version.bin \
6969 test-libaio.bin \
7070 test-libzstd.bin \
71− test-clang-bpf-global-var.bin \
71+ test-clang-bpf-co-re.bin \
7272 test-file-handle.bin \
7373 test-libpfm4.bin
7474
@@ -325,7 +325,7 @@ $(OUTPUT)test-libaio.bin: · 250 unmodified lines
325325 $(OUTPUT)test-libzstd.bin:
326326 $(BUILD) -lzstd
327327
328−$(OUTPUT)test-clang-bpf-global-var.bin:
328+$(OUTPUT)test-clang-bpf-co-re.bin:
329329 $(CLANG) -S -g -target bpf -o - $(patsubst %.bin,%.c,$(@F)) | \
330330 grep BTF_KIND_VAR
331331
tools/build/feature/test-clang-bpf-co-re.c+9−0
@@ -0,0 +1,9 @@
1+// SPDX-License-Identifier: GPL-2.0
2+// Copyright (c) 2020 Facebook
3+
4+struct test {
5+ int a;
6+ int b;
7+} __attribute__((preserve_access_index));
8+
9+volatile struct test global_value_for_test = {};
tools/build/feature/test-clang-bpf-global-var.c+0−4
@@ -1,4 +0,0 @@
1−// SPDX-License-Identifier: GPL-2.0
2−// Copyright (c) 2020 Facebook
3−
4−volatile int global_value_for_test = 1;
tools/lib/bpf/bpf_core_read.h+4−4
@@ -217,7 +217,7 @@ enum bpf_field_info_kind { · 216 unmodified lines
217217 */
218218 #define BPF_CORE_READ_INTO(dst, src, a, ...) \
219219 ({ \
220− ___core_read(bpf_core_read, dst, src, a, ##__VA_ARGS__) \
220+ ___core_read(bpf_core_read, dst, (src), a, ##__VA_ARGS__) \
221221 })
222222
223223 /*
@@ -227,7 +227,7 @@ enum bpf_field_info_kind { · 3 unmodified lines
227227 */
228228 #define BPF_CORE_READ_STR_INTO(dst, src, a, ...) \
229229 ({ \
230− ___core_read(bpf_core_read_str, dst, src, a, ##__VA_ARGS__) \
230+ ___core_read(bpf_core_read_str, dst, (src), a, ##__VA_ARGS__)\
231231 })
232232
233233 /*
@@ -254,8 +254,8 @@ enum bpf_field_info_kind { · 20 unmodified lines
254254 */
255255 #define BPF_CORE_READ(src, a, ...) \
256256 ({ \
257− ___type(src, a, ##__VA_ARGS__) __r; \
258− BPF_CORE_READ_INTO(&__r, src, a, ##__VA_ARGS__); \
257+ ___type((src), a, ##__VA_ARGS__) __r; \
258+ BPF_CORE_READ_INTO(&__r, (src), a, ##__VA_ARGS__); \
259259 __r; \
260260 })
261261
tools/lib/bpf/bpf_helpers.h+1−0
@@ -75,5 +75,6 @@ enum libbpf_tristate { · 74 unmodified lines
7575 };
7676
7777 #define __kconfig __attribute__((section(".kconfig")))
78+#define __ksym __attribute__((section(".ksyms")))
7879
7980 #endif
tools/lib/bpf/btf.h+5−0
@@ -168,6 +168,11 @@ static inline bool btf_kflag(const struct btf_type *t) · 167 unmodified lines
168168 return BTF_INFO_KFLAG(t->info);
169169 }
170170
171+static inline bool btf_is_void(const struct btf_type *t)
172+{
173+ return btf_kind(t) == BTF_KIND_UNKN;
174+}
175+
171176 static inline bool btf_is_int(const struct btf_type *t)
172177 {
173178 return btf_kind(t) == BTF_KIND_INT;
tools/lib/bpf/libbpf.c+340−142
@@ -285,6 +285,7 @@ struct bpf_struct_ops { · 284 unmodified lines
285285 #define BSS_SEC ".bss"
286286 #define RODATA_SEC ".rodata"
287287 #define KCONFIG_SEC ".kconfig"
288+#define KSYMS_SEC ".ksyms"
288289 #define STRUCT_OPS_SEC ".struct_ops"
289290
290291 enum libbpf_map_type {
@@ -330,24 +331,39 @@ struct bpf_map { · 39 unmodified lines
330331
331332 enum extern_type {
332333 EXT_UNKNOWN,
333− EXT_CHAR,
334− EXT_BOOL,
335− EXT_INT,
336− EXT_TRISTATE,
337− EXT_CHAR_ARR,
334+ EXT_KCFG,
335+ EXT_KSYM,
336+};
337+
338+enum kcfg_type {
339+ KCFG_UNKNOWN,
340+ KCFG_CHAR,
341+ KCFG_BOOL,
342+ KCFG_INT,
343+ KCFG_TRISTATE,
344+ KCFG_CHAR_ARR,
338345 };
339346
340347 struct extern_desc {
341− const char *name;
348+ enum extern_type type;
342349 int sym_idx;
343350 int btf_id;
344− enum extern_type type;
345− int sz;
346− int align;
347− int data_off;
348− bool is_signed;
349− bool is_weak;
351+ int sec_btf_id;
352+ const char *name;
350353 bool is_set;
354+ bool is_weak;
355+ union {
356+ struct {
357+ enum kcfg_type type;
358+ int sz;
359+ int align;
360+ int data_off;
361+ bool is_signed;
362+ } kcfg;
363+ struct {
364+ unsigned long long addr;
365+ } ksym;
366+ };
351367 };
352368
353369 static LIST_HEAD(bpf_objects_list);
@@ -1424,19 +1440,19 @@ static struct extern_desc *find_extern_by_name(const struct bpf_object *obj, · 1070 unmodified lines
14241440 return NULL;
14251441 }
14261442
1427−static int set_ext_value_tri(struct extern_desc *ext, void *ext_val,
1428− char value)
1443+static int set_kcfg_value_tri(struct extern_desc *ext, void *ext_val,
1444+ char value)
14291445 {
1430− switch (ext->type) {
1431− case EXT_BOOL:
1446+ switch (ext->kcfg.type) {
1447+ case KCFG_BOOL:
14321448 if (value == 'm') {
1433− pr_warn("extern %s=%c should be tristate or char\n",
1449+ pr_warn("extern (kcfg) %s=%c should be tristate or char\n",
14341450 ext->name, value);
14351451 return -EINVAL;
14361452 }
14371453 *(bool *)ext_val = value == 'y' ? true : false;
14381454 break;
1439− case EXT_TRISTATE:
1455+ case KCFG_TRISTATE:
14401456 if (value == 'y')
14411457 *(enum libbpf_tristate *)ext_val = TRI_YES;
14421458 else if (value == 'm')
@@ -1444,14 +1460,14 @@ static int set_ext_value_tri(struct extern_desc *ext, void *ext_val, · 1 unmodified line
14441460 else /* value == 'n' */
14451461 *(enum libbpf_tristate *)ext_val = TRI_NO;
14461462 break;
1447− case EXT_CHAR:
1463+ case KCFG_CHAR:
14481464 *(char *)ext_val = value;
14491465 break;
1450− case EXT_UNKNOWN:
1451− case EXT_INT:
1452− case EXT_CHAR_ARR:
1466+ case KCFG_UNKNOWN:
1467+ case KCFG_INT:
1468+ case KCFG_CHAR_ARR:
14531469 default:
1454− pr_warn("extern %s=%c should be bool, tristate, or char\n",
1470+ pr_warn("extern (kcfg) %s=%c should be bool, tristate, or char\n",
14551471 ext->name, value);
14561472 return -EINVAL;
14571473 }
@@ -1459,29 +1475,29 @@ static int set_ext_value_tri(struct extern_desc *ext, void *ext_val, · 1 unmodified line
14591475 return 0;
14601476 }
14611477
1462−static int set_ext_value_str(struct extern_desc *ext, char *ext_val,
1463− const char *value)
1478+static int set_kcfg_value_str(struct extern_desc *ext, char *ext_val,
1479+ const char *value)
14641480 {
14651481 size_t len;
14661482
1467− if (ext->type != EXT_CHAR_ARR) {
1468− pr_warn("extern %s=%s should char array\n", ext->name, value);
1483+ if (ext->kcfg.type != KCFG_CHAR_ARR) {
1484+ pr_warn("extern (kcfg) %s=%s should be char array\n", ext->name, value);
14691485 return -EINVAL;
14701486 }
14711487
14721488 len = strlen(value);
14731489 if (value[len - 1] != '"') {
1474− pr_warn("extern '%s': invalid string config '%s'\n",
1490+ pr_warn("extern (kcfg) '%s': invalid string config '%s'\n",
14751491 ext->name, value);
14761492 return -EINVAL;
14771493 }
14781494
14791495 /* strip quotes */
14801496 len -= 2;
1481− if (len >= ext->sz) {
1482− pr_warn("extern '%s': long string config %s of (%zu bytes) truncated to %d bytes\n",
1483− ext->name, value, len, ext->sz - 1);
1484− len = ext->sz - 1;
1497+ if (len >= ext->kcfg.sz) {
1498+ pr_warn("extern (kcfg) '%s': long string config %s of (%zu bytes) truncated to %d bytes\n",
1499+ ext->name, value, len, ext->kcfg.sz - 1);
1500+ len = ext->kcfg.sz - 1;
14851501 }
14861502 memcpy(ext_val, value + 1, len);
14871503 ext_val[len] = '\0';
@@ -1508,11 +1524,11 @@ static int parse_u64(const char *value, __u64 *res) · 20 unmodified lines
15081524 return 0;
15091525 }
15101526
1511−static bool is_ext_value_in_range(const struct extern_desc *ext, __u64 v)
1527+static bool is_kcfg_value_in_range(const struct extern_desc *ext, __u64 v)
15121528 {
1513− int bit_sz = ext->sz * 8;
1529+ int bit_sz = ext->kcfg.sz * 8;
15141530
1515− if (ext->sz == 8)
1531+ if (ext->kcfg.sz == 8)
15161532 return true;
15171533
15181534 /* Validate that value stored in u64 fits in integer of `ext->sz`
@@ -1527,26 +1543,26 @@ static bool is_ext_value_in_range(const struct extern_desc *ext, __u64 v) · 8 unmodified lines
15271543 * For unsigned target integer, check that all the (64 - Y) bits are
15281544 * zero.
15291545 */
1530− if (ext->is_signed)
1546+ if (ext->kcfg.is_signed)
15311547 return v + (1ULL << (bit_sz - 1)) < (1ULL << bit_sz);
15321548 else
15331549 return (v >> bit_sz) == 0;
15341550 }
15351551
1536−static int set_ext_value_num(struct extern_desc *ext, void *ext_val,
1537− __u64 value)
1552+static int set_kcfg_value_num(struct extern_desc *ext, void *ext_val,
1553+ __u64 value)
15381554 {
1539− if (ext->type != EXT_INT && ext->type != EXT_CHAR) {
1540− pr_warn("extern %s=%llu should be integer\n",
1555+ if (ext->kcfg.type != KCFG_INT && ext->kcfg.type != KCFG_CHAR) {
1556+ pr_warn("extern (kcfg) %s=%llu should be integer\n",
15411557 ext->name, (unsigned long long)value);
15421558 return -EINVAL;
15431559 }
1544− if (!is_ext_value_in_range(ext, value)) {
1545− pr_warn("extern %s=%llu value doesn't fit in %d bytes\n",
1546− ext->name, (unsigned long long)value, ext->sz);
1560+ if (!is_kcfg_value_in_range(ext, value)) {
1561+ pr_warn("extern (kcfg) %s=%llu value doesn't fit in %d bytes\n",
1562+ ext->name, (unsigned long long)value, ext->kcfg.sz);
15471563 return -ERANGE;
15481564 }
1549− switch (ext->sz) {
1565+ switch (ext->kcfg.sz) {
15501566 case 1: *(__u8 *)ext_val = value; break;
15511567 case 2: *(__u16 *)ext_val = value; break;
15521568 case 4: *(__u32 *)ext_val = value; break;
@@ -1592,30 +1608,30 @@ static int bpf_object__process_kconfig_line(struct bpf_object *obj, · 39 unmodified lines
15921608 if (!ext || ext->is_set)
15931609 return 0;
15941610
1595− ext_val = data + ext->data_off;
1611+ ext_val = data + ext->kcfg.data_off;
15961612 value = sep + 1;
15971613
15981614 switch (*value) {
15991615 case 'y': case 'n': case 'm':
1600− err = set_ext_value_tri(ext, ext_val, *value);
1616+ err = set_kcfg_value_tri(ext, ext_val, *value);
16011617 break;
16021618 case '"':
1603− err = set_ext_value_str(ext, ext_val, value);
1619+ err = set_kcfg_value_str(ext, ext_val, value);
16041620 break;
16051621 default:
16061622 /* assume integer */
16071623 err = parse_u64(value, &num);
16081624 if (err) {
1609− pr_warn("extern %s=%s should be integer\n",
1625+ pr_warn("extern (kcfg) %s=%s should be integer\n",
16101626 ext->name, value);
16111627 return err;
16121628 }
1613− err = set_ext_value_num(ext, ext_val, num);
1629+ err = set_kcfg_value_num(ext, ext_val, num);
16141630 break;
16151631 }
16161632 if (err)
16171633 return err;
1618− pr_debug("extern %s=%s\n", ext->name, value);
1634+ pr_debug("extern (kcfg) %s=%s\n", ext->name, value);
16191635 return 0;
16201636 }
16211637
@@ -1686,16 +1702,20 @@ static int bpf_object__read_kconfig_mem(struct bpf_object *obj, · 64 unmodified lines
16861702
16871703 static int bpf_object__init_kconfig_map(struct bpf_object *obj)
16881704 {
1689− struct extern_desc *last_ext;
1705+ struct extern_desc *last_ext = NULL, *ext;
16901706 size_t map_sz;
1691− int err;
1707+ int i, err;
16921708
1693− if (obj->nr_extern == 0)
1694− return 0;
1709+ for (i = 0; i < obj->nr_extern; i++) {
1710+ ext = &obj->externs[i];
1711+ if (ext->type == EXT_KCFG)
1712+ last_ext = ext;
1713+ }
16951714
1696− last_ext = &obj->externs[obj->nr_extern - 1];
1697− map_sz = last_ext->data_off + last_ext->sz;
1715+ if (!last_ext)
1716+ return 0;
16981717
1718+ map_sz = last_ext->kcfg.data_off + last_ext->kcfg.sz;
16991719 err = bpf_object__init_internal_map(obj, LIBBPF_MAP_KCONFIG,
17001720 obj->efile.symbols_shndx,
17011721 NULL, map_sz);
@@ -2714,8 +2734,33 @@ static int find_extern_btf_id(const struct btf *btf, const char *ext_name) · 1012 unmodified lines
27142734 return -ENOENT;
27152735 }
27162736
2717−static enum extern_type find_extern_type(const struct btf *btf, int id,
2718− bool *is_signed)
2737+static int find_extern_sec_btf_id(struct btf *btf, int ext_btf_id) {
2738+ const struct btf_var_secinfo *vs;
2739+ const struct btf_type *t;
2740+ int i, j, n;
2741+
2742+ if (!btf)
2743+ return -ESRCH;
2744+
2745+ n = btf__get_nr_types(btf);
2746+ for (i = 1; i <= n; i++) {
2747+ t = btf__type_by_id(btf, i);
2748+
2749+ if (!btf_is_datasec(t))
2750+ continue;
2751+
2752+ vs = btf_var_secinfos(t);
2753+ for (j = 0; j < btf_vlen(t); j++, vs++) {
2754+ if (vs->type == ext_btf_id)
2755+ return i;
2756+ }
2757+ }
2758+
2759+ return -ENOENT;
2760+}
2761+
2762+static enum kcfg_type find_kcfg_type(const struct btf *btf, int id,
2763+ bool *is_signed)
27192764 {
27202765 const struct btf_type *t;
27212766 const char *name;
@@ -2730,29 +2775,29 @@ static enum extern_type find_extern_type(const struct btf *btf, int id, · 8 unmodified lines
27302775 int enc = btf_int_encoding(t);
27312776
27322777 if (enc & BTF_INT_BOOL)
2733− return t->size == 1 ? EXT_BOOL : EXT_UNKNOWN;
2778+ return t->size == 1 ? KCFG_BOOL : KCFG_UNKNOWN;
27342779 if (is_signed)
27352780 *is_signed = enc & BTF_INT_SIGNED;
27362781 if (t->size == 1)
2737− return EXT_CHAR;
2782+ return KCFG_CHAR;
27382783 if (t->size < 1 || t->size > 8 || (t->size & (t->size - 1)))
2739− return EXT_UNKNOWN;
2740− return EXT_INT;
2784+ return KCFG_UNKNOWN;
2785+ return KCFG_INT;
27412786 }
27422787 case BTF_KIND_ENUM:
27432788 if (t->size != 4)
2744− return EXT_UNKNOWN;
2789+ return KCFG_UNKNOWN;
27452790 if (strcmp(name, "libbpf_tristate"))
2746− return EXT_UNKNOWN;
2747− return EXT_TRISTATE;
2791+ return KCFG_UNKNOWN;
2792+ return KCFG_TRISTATE;
27482793 case BTF_KIND_ARRAY:
27492794 if (btf_array(t)->nelems == 0)
2750− return EXT_UNKNOWN;
2751− if (find_extern_type(btf, btf_array(t)->type, NULL) != EXT_CHAR)
2752− return EXT_UNKNOWN;
2753− return EXT_CHAR_ARR;
2795+ return KCFG_UNKNOWN;
2796+ if (find_kcfg_type(btf, btf_array(t)->type, NULL) != KCFG_CHAR)
2797+ return KCFG_UNKNOWN;
2798+ return KCFG_CHAR_ARR;
27542799 default:
2755− return EXT_UNKNOWN;
2800+ return KCFG_UNKNOWN;
27562801 }
27572802 }
27582803
@@ -2761,23 +2806,45 @@ static int cmp_externs(const void *_a, const void *_b) · 2 unmodified lines
27612806 const struct extern_desc *a = _a;
27622807 const struct extern_desc *b = _b;
27632808
2764− /* descending order by alignment requirements */
2765− if (a->align != b->align)
2766− return a->align > b->align ? -1 : 1;
2767− /* ascending order by size, within same alignment class */
2768− if (a->sz != b->sz)
2769− return a->sz < b->sz ? -1 : 1;
2809+ if (a->type != b->type)
2810+ return a->type < b->type ? -1 : 1;
2811+
2812+ if (a->type == EXT_KCFG) {
2813+ /* descending order by alignment requirements */
2814+ if (a->kcfg.align != b->kcfg.align)
2815+ return a->kcfg.align > b->kcfg.align ? -1 : 1;
2816+ /* ascending order by size, within same alignment class */
2817+ if (a->kcfg.sz != b->kcfg.sz)
2818+ return a->kcfg.sz < b->kcfg.sz ? -1 : 1;
2819+ }
2820+
27702821 /* resolve ties by name */
27712822 return strcmp(a->name, b->name);
27722823 }
27732824
2825+static int find_int_btf_id(const struct btf *btf)
2826+{
2827+ const struct btf_type *t;
2828+ int i, n;
2829+
2830+ n = btf__get_nr_types(btf);
2831+ for (i = 1; i <= n; i++) {
2832+ t = btf__type_by_id(btf, i);
2833+
2834+ if (btf_is_int(t) && btf_int_bits(t) == 32)
2835+ return i;
2836+ }
2837+
2838+ return 0;
2839+}
2840+
27742841 static int bpf_object__collect_externs(struct bpf_object *obj)
27752842 {
2843+ struct btf_type *sec, *kcfg_sec = NULL, *ksym_sec = NULL;
27762844 const struct btf_type *t;
27772845 struct extern_desc *ext;
2778− int i, n, off, btf_id;
2779− struct btf_type *sec;
2780− const char *ext_name;
2846+ int i, n, off;
2847+ const char *ext_name, *sec_name;
27812848 Elf_Scn *scn;
27822849 GElf_Shdr sh;
27832850
@@ -2823,22 +2890,50 @@ static int bpf_object__collect_externs(struct bpf_object *obj) · 39 unmodified lines
28232890 ext->name = btf__name_by_offset(obj->btf, t->name_off);
28242891 ext->sym_idx = i;
28252892 ext->is_weak = GELF_ST_BIND(sym.st_info) == STB_WEAK;
2826− ext->sz = btf__resolve_size(obj->btf, t->type);
2827− if (ext->sz <= 0) {
2828− pr_warn("failed to resolve size of extern '%s': %d\n",
2829− ext_name, ext->sz);
2830− return ext->sz;
2831− }
2832− ext->align = btf__align_of(obj->btf, t->type);
2833− if (ext->align <= 0) {
2834− pr_warn("failed to determine alignment of extern '%s': %d\n",
2835− ext_name, ext->align);
2836− return -EINVAL;
2837− }
2838− ext->type = find_extern_type(obj->btf, t->type,
2839− &ext->is_signed);
2840− if (ext->type == EXT_UNKNOWN) {
2841− pr_warn("extern '%s' type is unsupported\n", ext_name);
2893+
2894+ ext->sec_btf_id = find_extern_sec_btf_id(obj->btf, ext->btf_id);
2895+ if (ext->sec_btf_id <= 0) {
2896+ pr_warn("failed to find BTF for extern '%s' [%d] section: %d\n",
2897+ ext_name, ext->btf_id, ext->sec_btf_id);
2898+ return ext->sec_btf_id;
2899+ }
2900+ sec = (void *)btf__type_by_id(obj->btf, ext->sec_btf_id);
2901+ sec_name = btf__name_by_offset(obj->btf, sec->name_off);
2902+
2903+ if (strcmp(sec_name, KCONFIG_SEC) == 0) {
2904+ kcfg_sec = sec;
2905+ ext->type = EXT_KCFG;
2906+ ext->kcfg.sz = btf__resolve_size(obj->btf, t->type);
2907+ if (ext->kcfg.sz <= 0) {
2908+ pr_warn("failed to resolve size of extern (kcfg) '%s': %d\n",
2909+ ext_name, ext->kcfg.sz);
2910+ return ext->kcfg.sz;
2911+ }
2912+ ext->kcfg.align = btf__align_of(obj->btf, t->type);
2913+ if (ext->kcfg.align <= 0) {
2914+ pr_warn("failed to determine alignment of extern (kcfg) '%s': %d\n",
2915+ ext_name, ext->kcfg.align);
2916+ return -EINVAL;
2917+ }
2918+ ext->kcfg.type = find_kcfg_type(obj->btf, t->type,
2919+ &ext->kcfg.is_signed);
2920+ if (ext->kcfg.type == KCFG_UNKNOWN) {
2921+ pr_warn("extern (kcfg) '%s' type is unsupported\n", ext_name);
2922+ return -ENOTSUP;
2923+ }
2924+ } else if (strcmp(sec_name, KSYMS_SEC) == 0) {
2925+ const struct btf_type *vt;
2926+
2927+ ksym_sec = sec;
2928+ ext->type = EXT_KSYM;
2929+
2930+ vt = skip_mods_and_typedefs(obj->btf, t->type, NULL);
2931+ if (!btf_is_void(vt)) {
2932+ pr_warn("extern (ksym) '%s' is not typeless (void)\n", ext_name);
2933+ return -ENOTSUP;
2934+ }
2935+ } else {
2936+ pr_warn("unrecognized extern section '%s'\n", sec_name);
28422937 return -ENOTSUP;
28432938 }
28442939 }
@@ -2847,42 +2942,80 @@ static int bpf_object__collect_externs(struct bpf_object *obj) · 2 unmodified lines
28472942 if (!obj->nr_extern)
28482943 return 0;
28492944
2850− /* sort externs by (alignment, size, name) and calculate their offsets
2851− * within a map */
2945+ /* sort externs by type, for kcfg ones also by (align, size, name) */
28522946 qsort(obj->externs, obj->nr_extern, sizeof(*ext), cmp_externs);
2853− off = 0;
2854− for (i = 0; i < obj->nr_extern; i++) {
2855− ext = &obj->externs[i];
2856− ext->data_off = roundup(off, ext->align);
2857− off = ext->data_off + ext->sz;
2858− pr_debug("extern #%d: symbol %d, off %u, name %s\n",
2859− i, ext->sym_idx, ext->data_off, ext->name);
2860− }
28612947
2862− btf_id = btf__find_by_name(obj->btf, KCONFIG_SEC);
2863− if (btf_id <= 0) {
2864− pr_warn("no BTF info found for '%s' datasec\n", KCONFIG_SEC);
2865− return -ESRCH;
2866− }
2948+ /* for .ksyms section, we need to turn all externs into allocated
2949+ * variables in BTF to pass kernel verification; we do this by
2950+ * pretending that each extern is a 8-byte variable
2951+ */
2952+ if (ksym_sec) {
2953+ /* find existing 4-byte integer type in BTF to use for fake
2954+ * extern variables in DATASEC
2955+ */
2956+ int int_btf_id = find_int_btf_id(obj->btf);
28672957
2868− sec = (struct btf_type *)btf__type_by_id(obj->btf, btf_id);
2869− sec->size = off;
2870− n = btf_vlen(sec);
2871− for (i = 0; i < n; i++) {
2872− struct btf_var_secinfo *vs = btf_var_secinfos(sec) + i;
2873−
2874− t = btf__type_by_id(obj->btf, vs->type);
2875− ext_name = btf__name_by_offset(obj->btf, t->name_off);
2876− ext = find_extern_by_name(obj, ext_name);
2877− if (!ext) {
2878− pr_warn("failed to find extern definition for BTF var '%s'\n",
2879− ext_name);
2880− return -ESRCH;
2958+ for (i = 0; i < obj->nr_extern; i++) {
2959+ ext = &obj->externs[i];
2960+ if (ext->type != EXT_KSYM)
2961+ continue;
2962+ pr_debug("extern (ksym) #%d: symbol %d, name %s\n",
2963+ i, ext->sym_idx, ext->name);
2964+ }
2965+
2966+ sec = ksym_sec;
2967+ n = btf_vlen(sec);
2968+ for (i = 0, off = 0; i < n; i++, off += sizeof(int)) {
2969+ struct btf_var_secinfo *vs = btf_var_secinfos(sec) + i;
2970+ struct btf_type *vt;
2971+
2972+ vt = (void *)btf__type_by_id(obj->btf, vs->type);
2973+ ext_name = btf__name_by_offset(obj->btf, vt->name_off);
2974+ ext = find_extern_by_name(obj, ext_name);
2975+ if (!ext) {
2976+ pr_warn("failed to find extern definition for BTF var '%s'\n",
2977+ ext_name);
2978+ return -ESRCH;
2979+ }
2980+ btf_var(vt)->linkage = BTF_VAR_GLOBAL_ALLOCATED;
2981+ vt->type = int_btf_id;
2982+ vs->offset = off;
2983+ vs->size = sizeof(int);
28812984 }
2882− vs->offset = ext->data_off;
2883− btf_var(t)->linkage = BTF_VAR_GLOBAL_ALLOCATED;
2985+ sec->size = off;
28842986 }
28852987
2988+ if (kcfg_sec) {
2989+ sec = kcfg_sec;
2990+ /* for kcfg externs calculate their offsets within a .kconfig map */
2991+ off = 0;
2992+ for (i = 0; i < obj->nr_extern; i++) {
2993+ ext = &obj->externs[i];
2994+ if (ext->type != EXT_KCFG)
2995+ continue;
2996+
2997+ ext->kcfg.data_off = roundup(off, ext->kcfg.align);
2998+ off = ext->kcfg.data_off + ext->kcfg.sz;
2999+ pr_debug("extern (kcfg) #%d: symbol %d, off %u, name %s\n",
3000+ i, ext->sym_idx, ext->kcfg.data_off, ext->name);
3001+ }
3002+ sec->size = off;
3003+ n = btf_vlen(sec);
3004+ for (i = 0; i < n; i++) {
3005+ struct btf_var_secinfo *vs = btf_var_secinfos(sec) + i;
3006+
3007+ t = btf__type_by_id(obj->btf, vs->type);
3008+ ext_name = btf__name_by_offset(obj->btf, t->name_off);
3009+ ext = find_extern_by_name(obj, ext_name);
3010+ if (!ext) {
3011+ pr_warn("failed to find extern definition for BTF var '%s'\n",
3012+ ext_name);
3013+ return -ESRCH;
3014+ }
3015+ btf_var(t)->linkage = BTF_VAR_GLOBAL_ALLOCATED;
3016+ vs->offset = ext->kcfg.data_off;
3017+ }
3018+ }
28863019 return 0;
28873020 }
28883021
@@ -3012,11 +3145,11 @@ static int bpf_program__record_reloc(struct bpf_program *prog, · 123 unmodified lines
30123145 sym_idx);
30133146 return -LIBBPF_ERRNO__RELOC;
30143147 }
3015− pr_debug("found extern #%d '%s' (sym %d, off %u) for insn %u\n",
3016− i, ext->name, ext->sym_idx, ext->data_off, insn_idx);
3148+ pr_debug("found extern #%d '%s' (sym %d) for insn %u\n",
3149+ i, ext->name, ext->sym_idx, insn_idx);
30173150 reloc_desc->type = RELO_EXTERN;
30183151 reloc_desc->insn_idx = insn_idx;
3019− reloc_desc->sym_off = ext->data_off;
3152+ reloc_desc->sym_off = i; /* sym_off stores extern index */
30203153 return 0;
30213154 }
30223155
@@ -4941,6 +5074,7 @@ bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj) · 1918 unmodified lines
49415074 for (i = 0; i < prog->nr_reloc; i++) {
49425075 struct reloc_desc *relo = &prog->reloc_desc[i];
49435076 struct bpf_insn *insn = &prog->insns[relo->insn_idx];
5077+ struct extern_desc *ext;
49445078
49455079 if (relo->insn_idx + 1 >= (int)prog->insns_cnt) {
49465080 pr_warn("relocation out of range: '%s'\n",
@@ -4959,9 +5093,15 @@ bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj) · 12 unmodified lines
49595093 insn[0].imm = obj->maps[relo->map_idx].fd;
49605094 break;
49615095 case RELO_EXTERN:
4962− insn[0].src_reg = BPF_PSEUDO_MAP_VALUE;
4963− insn[0].imm = obj->maps[obj->kconfig_map_idx].fd;
4964− insn[1].imm = relo->sym_off;
5096+ ext = &obj->externs[relo->sym_off];
5097+ if (ext->type == EXT_KCFG) {
5098+ insn[0].src_reg = BPF_PSEUDO_MAP_VALUE;
5099+ insn[0].imm = obj->maps[obj->kconfig_map_idx].fd;
5100+ insn[1].imm = ext->kcfg.data_off;
5101+ } else /* EXT_KSYM */ {
5102+ insn[0].imm = (__u32)ext->ksym.addr;
5103+ insn[1].imm = ext->ksym.addr >> 32;
5104+ }
49655105 break;
49665106 case RELO_CALL:
49675107 err = bpf_program__reloc_text(prog, obj, relo);
@@ -5580,56 +5720,114 @@ static int bpf_object__sanitize_maps(struct bpf_object *obj) · 612 unmodified lines
55805720 return 0;
55815721 }
55825722
5723+static int bpf_object__read_kallsyms_file(struct bpf_object *obj)
5724+{
5725+ char sym_type, sym_name[500];
5726+ unsigned long long sym_addr;
5727+ struct extern_desc *ext;
5728+ int ret, err = 0;
5729+ FILE *f;
5730+
5731+ f = fopen("/proc/kallsyms", "r");
5732+ if (!f) {
5733+ err = -errno;
5734+ pr_warn("failed to open /proc/kallsyms: %d\n", err);
5735+ return err;
5736+ }
5737+
5738+ while (true) {
5739+ ret = fscanf(f, "%llx %c %499s%*[^\n]\n",
5740+ &sym_addr, &sym_type, sym_name);
5741+ if (ret == EOF && feof(f))
5742+ break;
5743+ if (ret != 3) {
5744+ pr_warn("failed to read kallasyms entry: %d\n", ret);
5745+ err = -EINVAL;
5746+ goto out;
5747+ }
5748+
5749+ ext = find_extern_by_name(obj, sym_name);
5750+ if (!ext || ext->type != EXT_KSYM)
5751+ continue;
5752+
5753+ if (ext->is_set && ext->ksym.addr != sym_addr) {
5754+ pr_warn("extern (ksym) '%s' resolution is ambiguous: 0x%llx or 0x%llx\n",
5755+ sym_name, ext->ksym.addr, sym_addr);
5756+ err = -EINVAL;
5757+ goto out;
5758+ }
5759+ if (!ext->is_set) {
5760+ ext->is_set = true;
5761+ ext->ksym.addr = sym_addr;
5762+ pr_debug("extern (ksym) %s=0x%llx\n", sym_name, sym_addr);
5763+ }
5764+ }
5765+
5766+out:
5767+ fclose(f);
5768+ return err;
5769+}
5770+
55835771 static int bpf_object__resolve_externs(struct bpf_object *obj,
55845772 const char *extra_kconfig)
55855773 {
5586− bool need_config = false;
5774+ bool need_config = false, need_kallsyms = false;
55875775 struct extern_desc *ext;
5776+ void *kcfg_data = NULL;
55885777 int err, i;
5589− void *data;
55905778
55915779 if (obj->nr_extern == 0)
55925780 return 0;
55935781
5594− data = obj->maps[obj->kconfig_map_idx].mmaped;
5782+ if (obj->kconfig_map_idx >= 0)
5783+ kcfg_data = obj->maps[obj->kconfig_map_idx].mmaped;
55955784
55965785 for (i = 0; i < obj->nr_extern; i++) {
55975786 ext = &obj->externs[i];
55985787
5599− if (strcmp(ext->name, "LINUX_KERNEL_VERSION") == 0) {
5600− void *ext_val = data + ext->data_off;
5788+ if (ext->type == EXT_KCFG &&
5789+ strcmp(ext->name, "LINUX_KERNEL_VERSION") == 0) {
5790+ void *ext_val = kcfg_data + ext->kcfg.data_off;
56015791 __u32 kver = get_kernel_version();
56025792
56035793 if (!kver) {
56045794 pr_warn("failed to get kernel version\n");
56055795 return -EINVAL;
56065796 }
5607− err = set_ext_value_num(ext, ext_val, kver);
5797+ err = set_kcfg_value_num(ext, ext_val, kver);
56085798 if (err)
56095799 return err;
5610− pr_debug("extern %s=0x%x\n", ext->name, kver);
5611− } else if (strncmp(ext->name, "CONFIG_", 7) == 0) {
5800+ pr_debug("extern (kcfg) %s=0x%x\n", ext->name, kver);
5801+ } else if (ext->type == EXT_KCFG &&
5802+ strncmp(ext->name, "CONFIG_", 7) == 0) {
56125803 need_config = true;
5804+ } else if (ext->type == EXT_KSYM) {
5805+ need_kallsyms = true;
56135806 } else {
56145807 pr_warn("unrecognized extern '%s'\n", ext->name);
56155808 return -EINVAL;
56165809 }
56175810 }
56185811 if (need_config && extra_kconfig) {
5619− err = bpf_object__read_kconfig_mem(obj, extra_kconfig, data);
5812+ err = bpf_object__read_kconfig_mem(obj, extra_kconfig, kcfg_data);
56205813 if (err)
56215814 return -EINVAL;
56225815 need_config = false;
56235816 for (i = 0; i < obj->nr_extern; i++) {
56245817 ext = &obj->externs[i];
5625− if (!ext->is_set) {
5818+ if (ext->type == EXT_KCFG && !ext->is_set) {
56265819 need_config = true;
56275820 break;
56285821 }
56295822 }
56305823 }
56315824 if (need_config) {
5632− err = bpf_object__read_kconfig_file(obj, data);
5825+ err = bpf_object__read_kconfig_file(obj, kcfg_data);
5826+ if (err)
5827+ return -EINVAL;
5828+ }
5829+ if (need_kallsyms) {
5830+ err = bpf_object__read_kallsyms_file(obj);
56335831 if (err)
56345832 return -EINVAL;
56355833 }
tools/testing/selftests/bpf/prog_tests/ksyms.c+71−0
@@ -0,0 +1,71 @@
1+// SPDX-License-Identifier: GPL-2.0
2+/* Copyright (c) 2019 Facebook */
3+
4+#include <test_progs.h>
5+#include "test_ksyms.skel.h"
6+#include <sys/stat.h>
7+
8+static int duration;
9+
10+static __u64 kallsyms_find(const char *sym)
11+{
12+ char type, name[500];
13+ __u64 addr, res = 0;
14+ FILE *f;
15+
16+ f = fopen("/proc/kallsyms", "r");
17+ if (CHECK(!f, "kallsyms_fopen", "failed to open: %d\n", errno))
18+ return 0;
19+
20+ while (fscanf(f, "%llx %c %499s%*[^\n]\n", &addr, &type, name) > 0) {
21+ if (strcmp(name, sym) == 0) {
22+ res = addr;
23+ goto out;
24+ }
25+ }
26+
27+ CHECK(false, "not_found", "symbol %s not found\n", sym);
28+out:
29+ fclose(f);
30+ return res;
31+}
32+
33+void test_ksyms(void)
34+{
35+ __u64 link_fops_addr = kallsyms_find("bpf_link_fops");
36+ const char *btf_path = "/sys/kernel/btf/vmlinux";
37+ struct test_ksyms *skel;
38+ struct test_ksyms__data *data;
39+ struct stat st;
40+ __u64 btf_size;
41+ int err;
42+
43+ if (CHECK(stat(btf_path, &st), "stat_btf", "err %d\n", errno))
44+ return;
45+ btf_size = st.st_size;
46+
47+ skel = test_ksyms__open_and_load();
48+ if (CHECK(!skel, "skel_open", "failed to open and load skeleton\n"))
49+ return;
50+
51+ err = test_ksyms__attach(skel);
52+ if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err))
53+ goto cleanup;
54+
55+ /* trigger tracepoint */
56+ usleep(1);
57+
58+ data = skel->data;
59+ CHECK(data->out__bpf_link_fops != link_fops_addr, "bpf_link_fops",
60+ "got 0x%llx, exp 0x%llx\n",
61+ data->out__bpf_link_fops, link_fops_addr);
62+ CHECK(data->out__bpf_link_fops1 != 0, "bpf_link_fops1",
63+ "got %llu, exp %llu\n", data->out__bpf_link_fops1, (__u64)0);
64+ CHECK(data->out__btf_size != btf_size, "btf_size",
65+ "got %llu, exp %llu\n", data->out__btf_size, btf_size);
66+ CHECK(data->out__per_cpu_start != 0, "__per_cpu_start",
67+ "got %llu, exp %llu\n", data->out__per_cpu_start, (__u64)0);
68+
69+cleanup:
70+ test_ksyms__destroy(skel);
71+}
tools/testing/selftests/bpf/progs/test_ksyms.c+32−0
@@ -0,0 +1,32 @@
1+// SPDX-License-Identifier: GPL-2.0
2+/* Copyright (c) 2019 Facebook */
3+
4+#include <stdbool.h>
5+#include <linux/bpf.h>
6+#include <bpf/bpf_helpers.h>
7+
8+__u64 out__bpf_link_fops = -1;
9+__u64 out__bpf_link_fops1 = -1;
10+__u64 out__btf_size = -1;
11+__u64 out__per_cpu_start = -1;
12+
13+extern const void bpf_link_fops __ksym;
14+extern const void __start_BTF __ksym;
15+extern const void __stop_BTF __ksym;
16+extern const void __per_cpu_start __ksym;
17+/* non-existing symbol, weak, default to zero */
18+extern const void bpf_link_fops1 __ksym __weak;
19+
20+SEC("raw_tp/sys_enter")
21+int handler(const void *ctx)
22+{
23+ out__bpf_link_fops = (__u64)&bpf_link_fops;
24+ out__btf_size = (__u64)(&__stop_BTF - &__start_BTF);
25+ out__per_cpu_start = (__u64)&__per_cpu_start;
26+
27+ out__bpf_link_fops1 = (__u64)&bpf_link_fops1;
28+
29+ return 0;
30+}
31+
32+char _license[] SEC("license") = "GPL";