diff options
author | Alexei Starovoitov <ast@fb.com> | 2017-12-14 20:55:10 -0500 |
---|---|---|
committer | Daniel Borkmann <daniel@iogearbox.net> | 2017-12-17 14:34:35 -0500 |
commit | 48cca7e44f9f8268fdcd4351e2f19ff2275119d1 (patch) | |
tree | 2900d4f0541a70a9b734bcea775e310ceea068ae /tools/lib | |
parent | d98588cef04529aa326c6cbc0cfa01a3a3e00ef5 (diff) |
libbpf: add support for bpf_call
- recognize relocation emitted by llvm
- since all regular function will be kept in .text section and llvm
takes care of pc-relative offsets in bpf_call instruction
simply copy all of .text to relevant program section while adjusting
bpf_call instructions in program section to point to newly copied
body of instructions from .text
- do so for all programs in the elf file
- set all programs types to the one passed to bpf_prog_load()
Note for elf files with multiple programs that use different
functions in .text section we need to do 'linker' style logic.
This work is still TBD
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Diffstat (limited to 'tools/lib')
-rw-r--r-- | tools/lib/bpf/bpf.h | 2 | ||||
-rw-r--r-- | tools/lib/bpf/libbpf.c | 170 |
2 files changed, 128 insertions, 44 deletions
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 6534889e2b2f..9f44c196931e 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h | |||
@@ -40,7 +40,7 @@ int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name, | |||
40 | __u32 map_flags); | 40 | __u32 map_flags); |
41 | 41 | ||
42 | /* Recommend log buffer size */ | 42 | /* Recommend log buffer size */ |
43 | #define BPF_LOG_BUF_SIZE 65536 | 43 | #define BPF_LOG_BUF_SIZE (256 * 1024) |
44 | int bpf_load_program_name(enum bpf_prog_type type, const char *name, | 44 | int bpf_load_program_name(enum bpf_prog_type type, const char *name, |
45 | const struct bpf_insn *insns, | 45 | const struct bpf_insn *insns, |
46 | size_t insns_cnt, const char *license, | 46 | size_t insns_cnt, const char *license, |
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 65d0d0aff4fa..5b83875b3594 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c | |||
@@ -174,12 +174,19 @@ struct bpf_program { | |||
174 | char *name; | 174 | char *name; |
175 | char *section_name; | 175 | char *section_name; |
176 | struct bpf_insn *insns; | 176 | struct bpf_insn *insns; |
177 | size_t insns_cnt; | 177 | size_t insns_cnt, main_prog_cnt; |
178 | enum bpf_prog_type type; | 178 | enum bpf_prog_type type; |
179 | 179 | ||
180 | struct { | 180 | struct reloc_desc { |
181 | enum { | ||
182 | RELO_LD64, | ||
183 | RELO_CALL, | ||
184 | } type; | ||
181 | int insn_idx; | 185 | int insn_idx; |
182 | int map_idx; | 186 | union { |
187 | int map_idx; | ||
188 | int text_off; | ||
189 | }; | ||
183 | } *reloc_desc; | 190 | } *reloc_desc; |
184 | int nr_reloc; | 191 | int nr_reloc; |
185 | 192 | ||
@@ -234,6 +241,7 @@ struct bpf_object { | |||
234 | } *reloc; | 241 | } *reloc; |
235 | int nr_reloc; | 242 | int nr_reloc; |
236 | int maps_shndx; | 243 | int maps_shndx; |
244 | int text_shndx; | ||
237 | } efile; | 245 | } efile; |
238 | /* | 246 | /* |
239 | * All loaded bpf_object is linked in a list, which is | 247 | * All loaded bpf_object is linked in a list, which is |
@@ -375,9 +383,13 @@ bpf_object__init_prog_names(struct bpf_object *obj) | |||
375 | size_t pi, si; | 383 | size_t pi, si; |
376 | 384 | ||
377 | for (pi = 0; pi < obj->nr_programs; pi++) { | 385 | for (pi = 0; pi < obj->nr_programs; pi++) { |
378 | char *name = NULL; | 386 | const char *name = NULL; |
379 | 387 | ||
380 | prog = &obj->programs[pi]; | 388 | prog = &obj->programs[pi]; |
389 | if (prog->idx == obj->efile.text_shndx) { | ||
390 | name = ".text"; | ||
391 | goto skip_search; | ||
392 | } | ||
381 | 393 | ||
382 | for (si = 0; si < symbols->d_size / sizeof(GElf_Sym) && !name; | 394 | for (si = 0; si < symbols->d_size / sizeof(GElf_Sym) && !name; |
383 | si++) { | 395 | si++) { |
@@ -405,7 +417,7 @@ bpf_object__init_prog_names(struct bpf_object *obj) | |||
405 | prog->section_name); | 417 | prog->section_name); |
406 | return -EINVAL; | 418 | return -EINVAL; |
407 | } | 419 | } |
408 | 420 | skip_search: | |
409 | prog->name = strdup(name); | 421 | prog->name = strdup(name); |
410 | if (!prog->name) { | 422 | if (!prog->name) { |
411 | pr_warning("failed to allocate memory for prog sym %s\n", | 423 | pr_warning("failed to allocate memory for prog sym %s\n", |
@@ -795,6 +807,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj) | |||
795 | } else if ((sh.sh_type == SHT_PROGBITS) && | 807 | } else if ((sh.sh_type == SHT_PROGBITS) && |
796 | (sh.sh_flags & SHF_EXECINSTR) && | 808 | (sh.sh_flags & SHF_EXECINSTR) && |
797 | (data->d_size > 0)) { | 809 | (data->d_size > 0)) { |
810 | if (strcmp(name, ".text") == 0) | ||
811 | obj->efile.text_shndx = idx; | ||
798 | err = bpf_object__add_program(obj, data->d_buf, | 812 | err = bpf_object__add_program(obj, data->d_buf, |
799 | data->d_size, name, idx); | 813 | data->d_size, name, idx); |
800 | if (err) { | 814 | if (err) { |
@@ -856,11 +870,14 @@ bpf_object__find_prog_by_idx(struct bpf_object *obj, int idx) | |||
856 | } | 870 | } |
857 | 871 | ||
858 | static int | 872 | static int |
859 | bpf_program__collect_reloc(struct bpf_program *prog, | 873 | bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr, |
860 | size_t nr_maps, GElf_Shdr *shdr, | 874 | Elf_Data *data, struct bpf_object *obj) |
861 | Elf_Data *data, Elf_Data *symbols, | ||
862 | int maps_shndx, struct bpf_map *maps) | ||
863 | { | 875 | { |
876 | Elf_Data *symbols = obj->efile.symbols; | ||
877 | int text_shndx = obj->efile.text_shndx; | ||
878 | int maps_shndx = obj->efile.maps_shndx; | ||
879 | struct bpf_map *maps = obj->maps; | ||
880 | size_t nr_maps = obj->nr_maps; | ||
864 | int i, nrels; | 881 | int i, nrels; |
865 | 882 | ||
866 | pr_debug("collecting relocating info for: '%s'\n", | 883 | pr_debug("collecting relocating info for: '%s'\n", |
@@ -893,8 +910,10 @@ bpf_program__collect_reloc(struct bpf_program *prog, | |||
893 | GELF_R_SYM(rel.r_info)); | 910 | GELF_R_SYM(rel.r_info)); |
894 | return -LIBBPF_ERRNO__FORMAT; | 911 | return -LIBBPF_ERRNO__FORMAT; |
895 | } | 912 | } |
913 | pr_debug("relo for %ld value %ld name %d\n", | ||
914 | rel.r_info >> 32, sym.st_value, sym.st_name); | ||
896 | 915 | ||
897 | if (sym.st_shndx != maps_shndx) { | 916 | if (sym.st_shndx != maps_shndx && sym.st_shndx != text_shndx) { |
898 | pr_warning("Program '%s' contains non-map related relo data pointing to section %u\n", | 917 | pr_warning("Program '%s' contains non-map related relo data pointing to section %u\n", |
899 | prog->section_name, sym.st_shndx); | 918 | prog->section_name, sym.st_shndx); |
900 | return -LIBBPF_ERRNO__RELOC; | 919 | return -LIBBPF_ERRNO__RELOC; |
@@ -903,6 +922,17 @@ bpf_program__collect_reloc(struct bpf_program *prog, | |||
903 | insn_idx = rel.r_offset / sizeof(struct bpf_insn); | 922 | insn_idx = rel.r_offset / sizeof(struct bpf_insn); |
904 | pr_debug("relocation: insn_idx=%u\n", insn_idx); | 923 | pr_debug("relocation: insn_idx=%u\n", insn_idx); |
905 | 924 | ||
925 | if (insns[insn_idx].code == (BPF_JMP | BPF_CALL)) { | ||
926 | if (insns[insn_idx].src_reg != BPF_PSEUDO_CALL) { | ||
927 | pr_warning("incorrect bpf_call opcode\n"); | ||
928 | return -LIBBPF_ERRNO__RELOC; | ||
929 | } | ||
930 | prog->reloc_desc[i].type = RELO_CALL; | ||
931 | prog->reloc_desc[i].insn_idx = insn_idx; | ||
932 | prog->reloc_desc[i].text_off = sym.st_value; | ||
933 | continue; | ||
934 | } | ||
935 | |||
906 | if (insns[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) { | 936 | if (insns[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) { |
907 | pr_warning("bpf: relocation: invalid relo for insns[%d].code 0x%x\n", | 937 | pr_warning("bpf: relocation: invalid relo for insns[%d].code 0x%x\n", |
908 | insn_idx, insns[insn_idx].code); | 938 | insn_idx, insns[insn_idx].code); |
@@ -924,6 +954,7 @@ bpf_program__collect_reloc(struct bpf_program *prog, | |||
924 | return -LIBBPF_ERRNO__RELOC; | 954 | return -LIBBPF_ERRNO__RELOC; |
925 | } | 955 | } |
926 | 956 | ||
957 | prog->reloc_desc[i].type = RELO_LD64; | ||
927 | prog->reloc_desc[i].insn_idx = insn_idx; | 958 | prog->reloc_desc[i].insn_idx = insn_idx; |
928 | prog->reloc_desc[i].map_idx = map_idx; | 959 | prog->reloc_desc[i].map_idx = map_idx; |
929 | } | 960 | } |
@@ -963,27 +994,76 @@ bpf_object__create_maps(struct bpf_object *obj) | |||
963 | } | 994 | } |
964 | 995 | ||
965 | static int | 996 | static int |
997 | bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj, | ||
998 | struct reloc_desc *relo) | ||
999 | { | ||
1000 | struct bpf_insn *insn, *new_insn; | ||
1001 | struct bpf_program *text; | ||
1002 | size_t new_cnt; | ||
1003 | |||
1004 | if (relo->type != RELO_CALL) | ||
1005 | return -LIBBPF_ERRNO__RELOC; | ||
1006 | |||
1007 | if (prog->idx == obj->efile.text_shndx) { | ||
1008 | pr_warning("relo in .text insn %d into off %d\n", | ||
1009 | relo->insn_idx, relo->text_off); | ||
1010 | return -LIBBPF_ERRNO__RELOC; | ||
1011 | } | ||
1012 | |||
1013 | if (prog->main_prog_cnt == 0) { | ||
1014 | text = bpf_object__find_prog_by_idx(obj, obj->efile.text_shndx); | ||
1015 | if (!text) { | ||
1016 | pr_warning("no .text section found yet relo into text exist\n"); | ||
1017 | return -LIBBPF_ERRNO__RELOC; | ||
1018 | } | ||
1019 | new_cnt = prog->insns_cnt + text->insns_cnt; | ||
1020 | new_insn = realloc(prog->insns, new_cnt * sizeof(*insn)); | ||
1021 | if (!new_insn) { | ||
1022 | pr_warning("oom in prog realloc\n"); | ||
1023 | return -ENOMEM; | ||
1024 | } | ||
1025 | memcpy(new_insn + prog->insns_cnt, text->insns, | ||
1026 | text->insns_cnt * sizeof(*insn)); | ||
1027 | prog->insns = new_insn; | ||
1028 | prog->main_prog_cnt = prog->insns_cnt; | ||
1029 | prog->insns_cnt = new_cnt; | ||
1030 | } | ||
1031 | insn = &prog->insns[relo->insn_idx]; | ||
1032 | insn->imm += prog->main_prog_cnt - relo->insn_idx; | ||
1033 | pr_debug("added %zd insn from %s to prog %s\n", | ||
1034 | text->insns_cnt, text->section_name, prog->section_name); | ||
1035 | return 0; | ||
1036 | } | ||
1037 | |||
1038 | static int | ||
966 | bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj) | 1039 | bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj) |
967 | { | 1040 | { |
968 | int i; | 1041 | int i, err; |
969 | 1042 | ||
970 | if (!prog || !prog->reloc_desc) | 1043 | if (!prog || !prog->reloc_desc) |
971 | return 0; | 1044 | return 0; |
972 | 1045 | ||
973 | for (i = 0; i < prog->nr_reloc; i++) { | 1046 | for (i = 0; i < prog->nr_reloc; i++) { |
974 | int insn_idx, map_idx; | 1047 | if (prog->reloc_desc[i].type == RELO_LD64) { |
975 | struct bpf_insn *insns = prog->insns; | 1048 | struct bpf_insn *insns = prog->insns; |
1049 | int insn_idx, map_idx; | ||
976 | 1050 | ||
977 | insn_idx = prog->reloc_desc[i].insn_idx; | 1051 | insn_idx = prog->reloc_desc[i].insn_idx; |
978 | map_idx = prog->reloc_desc[i].map_idx; | 1052 | map_idx = prog->reloc_desc[i].map_idx; |
979 | 1053 | ||
980 | if (insn_idx >= (int)prog->insns_cnt) { | 1054 | if (insn_idx >= (int)prog->insns_cnt) { |
981 | pr_warning("relocation out of range: '%s'\n", | 1055 | pr_warning("relocation out of range: '%s'\n", |
982 | prog->section_name); | 1056 | prog->section_name); |
983 | return -LIBBPF_ERRNO__RELOC; | 1057 | return -LIBBPF_ERRNO__RELOC; |
1058 | } | ||
1059 | insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD; | ||
1060 | insns[insn_idx].imm = obj->maps[map_idx].fd; | ||
1061 | } else { | ||
1062 | err = bpf_program__reloc_text(prog, obj, | ||
1063 | &prog->reloc_desc[i]); | ||
1064 | if (err) | ||
1065 | return err; | ||
984 | } | 1066 | } |
985 | insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD; | ||
986 | insns[insn_idx].imm = obj->maps[map_idx].fd; | ||
987 | } | 1067 | } |
988 | 1068 | ||
989 | zfree(&prog->reloc_desc); | 1069 | zfree(&prog->reloc_desc); |
@@ -1026,7 +1106,6 @@ static int bpf_object__collect_reloc(struct bpf_object *obj) | |||
1026 | Elf_Data *data = obj->efile.reloc[i].data; | 1106 | Elf_Data *data = obj->efile.reloc[i].data; |
1027 | int idx = shdr->sh_info; | 1107 | int idx = shdr->sh_info; |
1028 | struct bpf_program *prog; | 1108 | struct bpf_program *prog; |
1029 | size_t nr_maps = obj->nr_maps; | ||
1030 | 1109 | ||
1031 | if (shdr->sh_type != SHT_REL) { | 1110 | if (shdr->sh_type != SHT_REL) { |
1032 | pr_warning("internal error at %d\n", __LINE__); | 1111 | pr_warning("internal error at %d\n", __LINE__); |
@@ -1040,11 +1119,9 @@ static int bpf_object__collect_reloc(struct bpf_object *obj) | |||
1040 | return -LIBBPF_ERRNO__RELOC; | 1119 | return -LIBBPF_ERRNO__RELOC; |
1041 | } | 1120 | } |
1042 | 1121 | ||
1043 | err = bpf_program__collect_reloc(prog, nr_maps, | 1122 | err = bpf_program__collect_reloc(prog, |
1044 | shdr, data, | 1123 | shdr, data, |
1045 | obj->efile.symbols, | 1124 | obj); |
1046 | obj->efile.maps_shndx, | ||
1047 | obj->maps); | ||
1048 | if (err) | 1125 | if (err) |
1049 | return err; | 1126 | return err; |
1050 | } | 1127 | } |
@@ -1197,6 +1274,8 @@ bpf_object__load_progs(struct bpf_object *obj) | |||
1197 | int err; | 1274 | int err; |
1198 | 1275 | ||
1199 | for (i = 0; i < obj->nr_programs; i++) { | 1276 | for (i = 0; i < obj->nr_programs; i++) { |
1277 | if (obj->programs[i].idx == obj->efile.text_shndx) | ||
1278 | continue; | ||
1200 | err = bpf_program__load(&obj->programs[i], | 1279 | err = bpf_program__load(&obj->programs[i], |
1201 | obj->license, | 1280 | obj->license, |
1202 | obj->kern_version); | 1281 | obj->kern_version); |
@@ -1859,7 +1938,7 @@ long libbpf_get_error(const void *ptr) | |||
1859 | int bpf_prog_load(const char *file, enum bpf_prog_type type, | 1938 | int bpf_prog_load(const char *file, enum bpf_prog_type type, |
1860 | struct bpf_object **pobj, int *prog_fd) | 1939 | struct bpf_object **pobj, int *prog_fd) |
1861 | { | 1940 | { |
1862 | struct bpf_program *prog; | 1941 | struct bpf_program *prog, *first_prog = NULL; |
1863 | struct bpf_object *obj; | 1942 | struct bpf_object *obj; |
1864 | int err; | 1943 | int err; |
1865 | 1944 | ||
@@ -1867,25 +1946,30 @@ int bpf_prog_load(const char *file, enum bpf_prog_type type, | |||
1867 | if (IS_ERR(obj)) | 1946 | if (IS_ERR(obj)) |
1868 | return -ENOENT; | 1947 | return -ENOENT; |
1869 | 1948 | ||
1870 | prog = bpf_program__next(NULL, obj); | 1949 | bpf_object__for_each_program(prog, obj) { |
1871 | if (!prog) { | 1950 | /* |
1872 | bpf_object__close(obj); | 1951 | * If type is not specified, try to guess it based on |
1873 | return -ENOENT; | 1952 | * section name. |
1874 | } | 1953 | */ |
1875 | |||
1876 | /* | ||
1877 | * If type is not specified, try to guess it based on | ||
1878 | * section name. | ||
1879 | */ | ||
1880 | if (type == BPF_PROG_TYPE_UNSPEC) { | ||
1881 | type = bpf_program__guess_type(prog); | ||
1882 | if (type == BPF_PROG_TYPE_UNSPEC) { | 1954 | if (type == BPF_PROG_TYPE_UNSPEC) { |
1883 | bpf_object__close(obj); | 1955 | type = bpf_program__guess_type(prog); |
1884 | return -EINVAL; | 1956 | if (type == BPF_PROG_TYPE_UNSPEC) { |
1957 | bpf_object__close(obj); | ||
1958 | return -EINVAL; | ||
1959 | } | ||
1885 | } | 1960 | } |
1961 | |||
1962 | bpf_program__set_type(prog, type); | ||
1963 | if (prog->idx != obj->efile.text_shndx && !first_prog) | ||
1964 | first_prog = prog; | ||
1965 | } | ||
1966 | |||
1967 | if (!first_prog) { | ||
1968 | pr_warning("object file doesn't contain bpf program\n"); | ||
1969 | bpf_object__close(obj); | ||
1970 | return -ENOENT; | ||
1886 | } | 1971 | } |
1887 | 1972 | ||
1888 | bpf_program__set_type(prog, type); | ||
1889 | err = bpf_object__load(obj); | 1973 | err = bpf_object__load(obj); |
1890 | if (err) { | 1974 | if (err) { |
1891 | bpf_object__close(obj); | 1975 | bpf_object__close(obj); |
@@ -1893,6 +1977,6 @@ int bpf_prog_load(const char *file, enum bpf_prog_type type, | |||
1893 | } | 1977 | } |
1894 | 1978 | ||
1895 | *pobj = obj; | 1979 | *pobj = obj; |
1896 | *prog_fd = bpf_program__fd(prog); | 1980 | *prog_fd = bpf_program__fd(first_prog); |
1897 | return 0; | 1981 | return 0; |
1898 | } | 1982 | } |