Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'extern-var-support'

Andrii Nakryiko says:

====================
It's often important for BPF program to know kernel version or some specific
config values (e.g., CONFIG_HZ to convert jiffies to seconds) and change or
adjust program logic based on their values. As of today, any such need has to
be resolved by recompiling BPF program for specific kernel and kernel
configuration. In practice this is usually achieved by using BCC and its
embedded LLVM/Clang. With such set up #ifdef CONFIG_XXX and similar
compile-time constructs allow to deal with kernel varieties.

With CO-RE (Compile Once – Run Everywhere) approach, this is not an option,
unfortunately. All such logic variations have to be done as a normal
C language constructs (i.e., if/else, variables, etc), not a preprocessor
directives. This patch series add support for such advanced scenarios through
C extern variables. These extern variables will be recognized by libbpf and
supplied through extra .extern internal map, similarly to global data. This
.extern map is read-only, which allows BPF verifier to track its content
precisely as constants. That gives an opportunity to have pre-compiled BPF
program, which can potentially use BPF functionality (e.g., BPF helpers) or
kernel features (types, fields, etc), that are available only on a subset of
targeted kernels, while effectively eleminating (through verifier's dead code
detection) such unsupported functionality for other kernels (typically, older
versions). Patch #3 explicitly tests a scenario of using unsupported BPF
helper, to validate the approach.

This patch set heavily relies on BTF type information emitted by compiler for
each extern variable declaration. Based on specific types, libbpf does strict
checks of config data values correctness. See patch #1 for details.

Outline of the patch set:
- patch #1 does a small clean up of internal map names contants;
- patch #2 adds all of the libbpf internal machinery for externs support,
including setting up BTF information for .extern data section;
- patch #3 adds support for .extern into BPF skeleton;
- patch #4 adds externs selftests, as well as enhances test_skeleton.c test to
validate mmap()-ed .extern datasection functionality.

v3->v4:
- clean up copyrights and rebase onto latest skeleton patches (Alexei);

v2->v3:
- truncate too long strings (Alexei);
- clean ups, adding comments (Alexei);

v1->v2:
- use BTF type information for externs (Alexei);
- add strings support;
- add BPF skeleton support for .extern.
====================

Signed-off-by: Alexei Starovoitov <ast@kernel.org>

+1035 -81
+2 -1
include/uapi/linux/btf.h
··· 142 142 143 143 enum { 144 144 BTF_VAR_STATIC = 0, 145 - BTF_VAR_GLOBAL_ALLOCATED, 145 + BTF_VAR_GLOBAL_ALLOCATED = 1, 146 + BTF_VAR_GLOBAL_EXTERN = 2, 146 147 }; 147 148 148 149 /* BTF_KIND_VAR is followed by a single "struct btf_var" to describe
+4
tools/bpf/bpftool/gen.c
··· 82 82 return "rodata"; 83 83 else if (str_has_suffix(name, ".bss")) 84 84 return "bss"; 85 + else if (str_has_suffix(name, ".extern")) 86 + return "externs"; /* extern is a C keyword */ 85 87 else 86 88 return NULL; 87 89 } ··· 111 109 sec_ident = "bss"; 112 110 else if (strcmp(sec_name, ".rodata") == 0) 113 111 sec_ident = "rodata"; 112 + else if (strcmp(sec_name, ".extern") == 0) 113 + sec_ident = "externs"; /* extern is a C keyword */ 114 114 else 115 115 return 0; 116 116
+4 -3
tools/include/uapi/linux/btf.h
··· 22 22 }; 23 23 24 24 /* Max # of type identifier */ 25 - #define BTF_MAX_TYPE 0x0000ffff 25 + #define BTF_MAX_TYPE 0x000fffff 26 26 /* Max offset into the string section */ 27 - #define BTF_MAX_NAME_OFFSET 0x0000ffff 27 + #define BTF_MAX_NAME_OFFSET 0x00ffffff 28 28 /* Max # of struct/union/enum members or func args */ 29 29 #define BTF_MAX_VLEN 0xffff 30 30 ··· 142 142 143 143 enum { 144 144 BTF_VAR_STATIC = 0, 145 - BTF_VAR_GLOBAL_ALLOCATED, 145 + BTF_VAR_GLOBAL_ALLOCATED = 1, 146 + BTF_VAR_GLOBAL_EXTERN = 2, 146 147 }; 147 148 148 149 /* BTF_KIND_VAR is followed by a single "struct btf_var" to describe
+9 -6
tools/lib/bpf/Makefile
··· 56 56 endif 57 57 58 58 FEATURE_USER = .libbpf 59 - FEATURE_TESTS = libelf libelf-mmap bpf reallocarray 60 - FEATURE_DISPLAY = libelf bpf 59 + FEATURE_TESTS = libelf libelf-mmap zlib bpf reallocarray 60 + FEATURE_DISPLAY = libelf zlib bpf 61 61 62 62 INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(ARCH)/include/uapi -I$(srctree)/tools/include/uapi 63 63 FEATURE_CHECK_CFLAGS-bpf = $(INCLUDES) ··· 160 160 161 161 all_cmd: $(CMD_TARGETS) check 162 162 163 - $(BPF_IN_SHARED): force elfdep bpfdep bpf_helper_defs.h 163 + $(BPF_IN_SHARED): force elfdep zdep bpfdep bpf_helper_defs.h 164 164 @(test -f ../../include/uapi/linux/bpf.h -a -f ../../../include/uapi/linux/bpf.h && ( \ 165 165 (diff -B ../../include/uapi/linux/bpf.h ../../../include/uapi/linux/bpf.h >/dev/null) || \ 166 166 echo "Warning: Kernel ABI header at 'tools/include/uapi/linux/bpf.h' differs from latest version at 'include/uapi/linux/bpf.h'" >&2 )) || true ··· 178 178 echo "Warning: Kernel ABI header at 'tools/include/uapi/linux/if_xdp.h' differs from latest version at 'include/uapi/linux/if_xdp.h'" >&2 )) || true 179 179 $(Q)$(MAKE) $(build)=libbpf OUTPUT=$(SHARED_OBJDIR) CFLAGS="$(CFLAGS) $(SHLIB_FLAGS)" 180 180 181 - $(BPF_IN_STATIC): force elfdep bpfdep bpf_helper_defs.h 181 + $(BPF_IN_STATIC): force elfdep zdep bpfdep bpf_helper_defs.h 182 182 $(Q)$(MAKE) $(build)=libbpf OUTPUT=$(STATIC_OBJDIR) 183 183 184 184 bpf_helper_defs.h: $(srctree)/tools/include/uapi/linux/bpf.h ··· 190 190 $(OUTPUT)libbpf.so.$(LIBBPF_VERSION): $(BPF_IN_SHARED) 191 191 $(QUIET_LINK)$(CC) $(LDFLAGS) \ 192 192 --shared -Wl,-soname,libbpf.so.$(LIBBPF_MAJOR_VERSION) \ 193 - -Wl,--version-script=$(VERSION_SCRIPT) $^ -lelf -o $@ 193 + -Wl,--version-script=$(VERSION_SCRIPT) $^ -lelf -lz -o $@ 194 194 @ln -sf $(@F) $(OUTPUT)libbpf.so 195 195 @ln -sf $(@F) $(OUTPUT)libbpf.so.$(LIBBPF_MAJOR_VERSION) 196 196 ··· 279 279 280 280 281 281 282 - PHONY += force elfdep bpfdep cscope tags 282 + PHONY += force elfdep zdep bpfdep cscope tags 283 283 force: 284 284 285 285 elfdep: 286 286 @if [ "$(feature-libelf)" != "1" ]; then echo "No libelf found"; exit 1 ; fi 287 + 288 + zdep: 289 + @if [ "$(feature-zlib)" != "1" ]; then echo "No zlib found"; exit 1 ; fi 287 290 288 291 bpfdep: 289 292 @if [ "$(feature-bpf)" != "1" ]; then echo "BPF API too old"; exit 1 ; fi
+9
tools/lib/bpf/bpf_helpers.h
··· 25 25 #ifndef __always_inline 26 26 #define __always_inline __attribute__((always_inline)) 27 27 #endif 28 + #ifndef __weak 29 + #define __weak __attribute__((weak)) 30 + #endif 28 31 29 32 /* 30 33 * Helper structure used by eBPF C program ··· 45 42 LIBBPF_PIN_NONE, 46 43 /* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */ 47 44 LIBBPF_PIN_BY_NAME, 45 + }; 46 + 47 + enum libbpf_tristate { 48 + TRI_NO = 0, 49 + TRI_YES = 1, 50 + TRI_MODULE = 2, 48 51 }; 49 52 50 53 #endif
+8 -1
tools/lib/bpf/btf.c
··· 578 578 return -ENOENT; 579 579 } 580 580 581 + /* .extern datasec size and var offsets were set correctly during 582 + * extern collection step, so just skip straight to sorting variables 583 + */ 584 + if (t->size) 585 + goto sort_vars; 586 + 581 587 ret = bpf_object__section_size(obj, name, &size); 582 588 if (ret || !size || (t->size && t->size != size)) { 583 589 pr_debug("Invalid size for section %s: %u bytes\n", name, size); ··· 620 614 vsi->offset = off; 621 615 } 622 616 623 - qsort(t + 1, vars, sizeof(*vsi), compare_vsi_off); 617 + sort_vars: 618 + qsort(btf_var_secinfos(t), vars, sizeof(*vsi), compare_vsi_off); 624 619 return 0; 625 620 } 626 621
+704 -67
tools/lib/bpf/libbpf.c
··· 44 44 #include <tools/libc_compat.h> 45 45 #include <libelf.h> 46 46 #include <gelf.h> 47 + #include <zlib.h> 47 48 48 49 #include "libbpf.h" 49 50 #include "bpf.h" ··· 140 139 __u32 array_mmap:1; 141 140 }; 142 141 142 + enum reloc_type { 143 + RELO_LD64, 144 + RELO_CALL, 145 + RELO_DATA, 146 + RELO_EXTERN, 147 + }; 148 + 149 + struct reloc_desc { 150 + enum reloc_type type; 151 + int insn_idx; 152 + int map_idx; 153 + int sym_off; 154 + }; 155 + 143 156 /* 144 157 * bpf_prog should be a better name but it has been used in 145 158 * linux/filter.h. ··· 172 157 size_t insns_cnt, main_prog_cnt; 173 158 enum bpf_prog_type type; 174 159 175 - struct reloc_desc { 176 - enum { 177 - RELO_LD64, 178 - RELO_CALL, 179 - RELO_DATA, 180 - } type; 181 - int insn_idx; 182 - int map_idx; 183 - int sym_off; 184 - } *reloc_desc; 160 + struct reloc_desc *reloc_desc; 185 161 int nr_reloc; 186 162 int log_level; 187 163 ··· 201 195 __u32 prog_flags; 202 196 }; 203 197 198 + #define DATA_SEC ".data" 199 + #define BSS_SEC ".bss" 200 + #define RODATA_SEC ".rodata" 201 + #define EXTERN_SEC ".extern" 202 + 204 203 enum libbpf_map_type { 205 204 LIBBPF_MAP_UNSPEC, 206 205 LIBBPF_MAP_DATA, 207 206 LIBBPF_MAP_BSS, 208 207 LIBBPF_MAP_RODATA, 208 + LIBBPF_MAP_EXTERN, 209 209 }; 210 210 211 211 static const char * const libbpf_type_to_btf_name[] = { 212 - [LIBBPF_MAP_DATA] = ".data", 213 - [LIBBPF_MAP_BSS] = ".bss", 214 - [LIBBPF_MAP_RODATA] = ".rodata", 212 + [LIBBPF_MAP_DATA] = DATA_SEC, 213 + [LIBBPF_MAP_BSS] = BSS_SEC, 214 + [LIBBPF_MAP_RODATA] = RODATA_SEC, 215 + [LIBBPF_MAP_EXTERN] = EXTERN_SEC, 215 216 }; 216 217 217 218 struct bpf_map { ··· 240 227 bool reused; 241 228 }; 242 229 230 + enum extern_type { 231 + EXT_UNKNOWN, 232 + EXT_CHAR, 233 + EXT_BOOL, 234 + EXT_INT, 235 + EXT_TRISTATE, 236 + EXT_CHAR_ARR, 237 + }; 238 + 239 + struct extern_desc { 240 + const char *name; 241 + int sym_idx; 242 + int btf_id; 243 + enum extern_type type; 244 + int sz; 245 + int align; 246 + int data_off; 247 + bool is_signed; 248 + bool is_weak; 249 + bool is_set; 250 + }; 251 + 243 252 static LIST_HEAD(bpf_objects_list); 244 253 245 254 struct bpf_object { ··· 274 239 struct bpf_map *maps; 275 240 size_t nr_maps; 276 241 size_t maps_cap; 242 + 243 + char *kconfig_path; 244 + struct extern_desc *externs; 245 + int nr_extern; 246 + int extern_map_idx; 277 247 278 248 bool loaded; 279 249 bool has_pseudo_calls; ··· 307 267 int maps_shndx; 308 268 int btf_maps_shndx; 309 269 int text_shndx; 270 + int symbols_shndx; 310 271 int data_shndx; 311 272 int rodata_shndx; 312 273 int bss_shndx; ··· 579 538 obj->efile.data_shndx = -1; 580 539 obj->efile.rodata_shndx = -1; 581 540 obj->efile.bss_shndx = -1; 541 + obj->extern_map_idx = -1; 582 542 583 543 obj->kern_version = get_kernel_version(); 584 544 obj->loaded = false; ··· 778 736 *size = 0; 779 737 if (!name) { 780 738 return -EINVAL; 781 - } else if (!strcmp(name, ".data")) { 739 + } else if (!strcmp(name, DATA_SEC)) { 782 740 if (obj->efile.data) 783 741 *size = obj->efile.data->d_size; 784 - } else if (!strcmp(name, ".bss")) { 742 + } else if (!strcmp(name, BSS_SEC)) { 785 743 if (obj->efile.bss) 786 744 *size = obj->efile.bss->d_size; 787 - } else if (!strcmp(name, ".rodata")) { 745 + } else if (!strcmp(name, RODATA_SEC)) { 788 746 if (obj->efile.rodata) 789 747 *size = obj->efile.rodata->d_size; 790 748 } else { ··· 904 862 def->key_size = sizeof(int); 905 863 def->value_size = data_sz; 906 864 def->max_entries = 1; 907 - def->map_flags = type == LIBBPF_MAP_RODATA ? BPF_F_RDONLY_PROG : 0; 865 + def->map_flags = type == LIBBPF_MAP_RODATA || type == LIBBPF_MAP_EXTERN 866 + ? BPF_F_RDONLY_PROG : 0; 908 867 def->map_flags |= BPF_F_MMAPABLE; 909 868 910 869 pr_debug("map '%s' (global data): at sec_idx %d, offset %zu, flags %x.\n", ··· 922 879 return err; 923 880 } 924 881 925 - if (type != LIBBPF_MAP_BSS) 882 + if (data) 926 883 memcpy(map->mmaped, data, data_sz); 927 884 928 885 pr_debug("map %td is \"%s\"\n", map - obj->maps, map->name); ··· 960 917 if (err) 961 918 return err; 962 919 } 920 + return 0; 921 + } 922 + 923 + 924 + static struct extern_desc *find_extern_by_name(const struct bpf_object *obj, 925 + const void *name) 926 + { 927 + int i; 928 + 929 + for (i = 0; i < obj->nr_extern; i++) { 930 + if (strcmp(obj->externs[i].name, name) == 0) 931 + return &obj->externs[i]; 932 + } 933 + return NULL; 934 + } 935 + 936 + static int set_ext_value_tri(struct extern_desc *ext, void *ext_val, 937 + char value) 938 + { 939 + switch (ext->type) { 940 + case EXT_BOOL: 941 + if (value == 'm') { 942 + pr_warn("extern %s=%c should be tristate or char\n", 943 + ext->name, value); 944 + return -EINVAL; 945 + } 946 + *(bool *)ext_val = value == 'y' ? true : false; 947 + break; 948 + case EXT_TRISTATE: 949 + if (value == 'y') 950 + *(enum libbpf_tristate *)ext_val = TRI_YES; 951 + else if (value == 'm') 952 + *(enum libbpf_tristate *)ext_val = TRI_MODULE; 953 + else /* value == 'n' */ 954 + *(enum libbpf_tristate *)ext_val = TRI_NO; 955 + break; 956 + case EXT_CHAR: 957 + *(char *)ext_val = value; 958 + break; 959 + case EXT_UNKNOWN: 960 + case EXT_INT: 961 + case EXT_CHAR_ARR: 962 + default: 963 + pr_warn("extern %s=%c should be bool, tristate, or char\n", 964 + ext->name, value); 965 + return -EINVAL; 966 + } 967 + ext->is_set = true; 968 + return 0; 969 + } 970 + 971 + static int set_ext_value_str(struct extern_desc *ext, char *ext_val, 972 + const char *value) 973 + { 974 + size_t len; 975 + 976 + if (ext->type != EXT_CHAR_ARR) { 977 + pr_warn("extern %s=%s should char array\n", ext->name, value); 978 + return -EINVAL; 979 + } 980 + 981 + len = strlen(value); 982 + if (value[len - 1] != '"') { 983 + pr_warn("extern '%s': invalid string config '%s'\n", 984 + ext->name, value); 985 + return -EINVAL; 986 + } 987 + 988 + /* strip quotes */ 989 + len -= 2; 990 + if (len >= ext->sz) { 991 + pr_warn("extern '%s': long string config %s of (%zu bytes) truncated to %d bytes\n", 992 + ext->name, value, len, ext->sz - 1); 993 + len = ext->sz - 1; 994 + } 995 + memcpy(ext_val, value + 1, len); 996 + ext_val[len] = '\0'; 997 + ext->is_set = true; 998 + return 0; 999 + } 1000 + 1001 + static int parse_u64(const char *value, __u64 *res) 1002 + { 1003 + char *value_end; 1004 + int err; 1005 + 1006 + errno = 0; 1007 + *res = strtoull(value, &value_end, 0); 1008 + if (errno) { 1009 + err = -errno; 1010 + pr_warn("failed to parse '%s' as integer: %d\n", value, err); 1011 + return err; 1012 + } 1013 + if (*value_end) { 1014 + pr_warn("failed to parse '%s' as integer completely\n", value); 1015 + return -EINVAL; 1016 + } 1017 + return 0; 1018 + } 1019 + 1020 + static bool is_ext_value_in_range(const struct extern_desc *ext, __u64 v) 1021 + { 1022 + int bit_sz = ext->sz * 8; 1023 + 1024 + if (ext->sz == 8) 1025 + return true; 1026 + 1027 + /* Validate that value stored in u64 fits in integer of `ext->sz` 1028 + * bytes size without any loss of information. If the target integer 1029 + * is signed, we rely on the following limits of integer type of 1030 + * Y bits and subsequent transformation: 1031 + * 1032 + * -2^(Y-1) <= X <= 2^(Y-1) - 1 1033 + * 0 <= X + 2^(Y-1) <= 2^Y - 1 1034 + * 0 <= X + 2^(Y-1) < 2^Y 1035 + * 1036 + * For unsigned target integer, check that all the (64 - Y) bits are 1037 + * zero. 1038 + */ 1039 + if (ext->is_signed) 1040 + return v + (1ULL << (bit_sz - 1)) < (1ULL << bit_sz); 1041 + else 1042 + return (v >> bit_sz) == 0; 1043 + } 1044 + 1045 + static int set_ext_value_num(struct extern_desc *ext, void *ext_val, 1046 + __u64 value) 1047 + { 1048 + if (ext->type != EXT_INT && ext->type != EXT_CHAR) { 1049 + pr_warn("extern %s=%llu should be integer\n", 1050 + ext->name, value); 1051 + return -EINVAL; 1052 + } 1053 + if (!is_ext_value_in_range(ext, value)) { 1054 + pr_warn("extern %s=%llu value doesn't fit in %d bytes\n", 1055 + ext->name, value, ext->sz); 1056 + return -ERANGE; 1057 + } 1058 + switch (ext->sz) { 1059 + case 1: *(__u8 *)ext_val = value; break; 1060 + case 2: *(__u16 *)ext_val = value; break; 1061 + case 4: *(__u32 *)ext_val = value; break; 1062 + case 8: *(__u64 *)ext_val = value; break; 1063 + default: 1064 + return -EINVAL; 1065 + } 1066 + ext->is_set = true; 1067 + return 0; 1068 + } 1069 + 1070 + static int bpf_object__read_kernel_config(struct bpf_object *obj, 1071 + const char *config_path, 1072 + void *data) 1073 + { 1074 + char buf[PATH_MAX], *sep, *value; 1075 + struct extern_desc *ext; 1076 + int len, err = 0; 1077 + void *ext_val; 1078 + __u64 num; 1079 + gzFile file; 1080 + 1081 + if (config_path) { 1082 + file = gzopen(config_path, "r"); 1083 + } else { 1084 + struct utsname uts; 1085 + 1086 + uname(&uts); 1087 + len = snprintf(buf, PATH_MAX, "/boot/config-%s", uts.release); 1088 + if (len < 0) 1089 + return -EINVAL; 1090 + else if (len >= PATH_MAX) 1091 + return -ENAMETOOLONG; 1092 + /* gzopen also accepts uncompressed files. */ 1093 + file = gzopen(buf, "r"); 1094 + if (!file) 1095 + file = gzopen("/proc/config.gz", "r"); 1096 + } 1097 + if (!file) { 1098 + pr_warn("failed to read kernel config at '%s'\n", config_path); 1099 + return -ENOENT; 1100 + } 1101 + 1102 + while (gzgets(file, buf, sizeof(buf))) { 1103 + if (strncmp(buf, "CONFIG_", 7)) 1104 + continue; 1105 + 1106 + sep = strchr(buf, '='); 1107 + if (!sep) { 1108 + err = -EINVAL; 1109 + pr_warn("failed to parse '%s': no separator\n", buf); 1110 + goto out; 1111 + } 1112 + /* Trim ending '\n' */ 1113 + len = strlen(buf); 1114 + if (buf[len - 1] == '\n') 1115 + buf[len - 1] = '\0'; 1116 + /* Split on '=' and ensure that a value is present. */ 1117 + *sep = '\0'; 1118 + if (!sep[1]) { 1119 + err = -EINVAL; 1120 + *sep = '='; 1121 + pr_warn("failed to parse '%s': no value\n", buf); 1122 + goto out; 1123 + } 1124 + 1125 + ext = find_extern_by_name(obj, buf); 1126 + if (!ext) 1127 + continue; 1128 + if (ext->is_set) { 1129 + err = -EINVAL; 1130 + pr_warn("re-defining extern '%s' not allowed\n", buf); 1131 + goto out; 1132 + } 1133 + 1134 + ext_val = data + ext->data_off; 1135 + value = sep + 1; 1136 + 1137 + switch (*value) { 1138 + case 'y': case 'n': case 'm': 1139 + err = set_ext_value_tri(ext, ext_val, *value); 1140 + break; 1141 + case '"': 1142 + err = set_ext_value_str(ext, ext_val, value); 1143 + break; 1144 + default: 1145 + /* assume integer */ 1146 + err = parse_u64(value, &num); 1147 + if (err) { 1148 + pr_warn("extern %s=%s should be integer\n", 1149 + ext->name, value); 1150 + goto out; 1151 + } 1152 + err = set_ext_value_num(ext, ext_val, num); 1153 + break; 1154 + } 1155 + if (err) 1156 + goto out; 1157 + pr_debug("extern %s=%s\n", ext->name, value); 1158 + } 1159 + 1160 + out: 1161 + gzclose(file); 1162 + return err; 1163 + } 1164 + 1165 + static int bpf_object__init_extern_map(struct bpf_object *obj) 1166 + { 1167 + struct extern_desc *last_ext; 1168 + size_t map_sz; 1169 + int err; 1170 + 1171 + if (obj->nr_extern == 0) 1172 + return 0; 1173 + 1174 + last_ext = &obj->externs[obj->nr_extern - 1]; 1175 + map_sz = last_ext->data_off + last_ext->sz; 1176 + 1177 + err = bpf_object__init_internal_map(obj, LIBBPF_MAP_EXTERN, 1178 + obj->efile.symbols_shndx, 1179 + NULL, map_sz); 1180 + if (err) 1181 + return err; 1182 + 1183 + obj->extern_map_idx = obj->nr_maps - 1; 1184 + 963 1185 return 0; 964 1186 } 965 1187 ··· 1705 1397 static int bpf_object__init_maps(struct bpf_object *obj, 1706 1398 const struct bpf_object_open_opts *opts) 1707 1399 { 1708 - const char *pin_root_path = OPTS_GET(opts, pin_root_path, NULL); 1709 - bool strict = !OPTS_GET(opts, relaxed_maps, false); 1400 + const char *pin_root_path; 1401 + bool strict; 1710 1402 int err; 1711 1403 1404 + strict = !OPTS_GET(opts, relaxed_maps, false); 1405 + pin_root_path = OPTS_GET(opts, pin_root_path, NULL); 1406 + 1712 1407 err = bpf_object__init_user_maps(obj, strict); 1713 - if (err) 1714 - return err; 1715 - 1716 - err = bpf_object__init_user_btf_maps(obj, strict, pin_root_path); 1717 - if (err) 1718 - return err; 1719 - 1720 - err = bpf_object__init_global_data_maps(obj); 1408 + err = err ?: bpf_object__init_user_btf_maps(obj, strict, pin_root_path); 1409 + err = err ?: bpf_object__init_global_data_maps(obj); 1410 + err = err ?: bpf_object__init_extern_map(obj); 1721 1411 if (err) 1722 1412 return err; 1723 1413 ··· 1834 1528 BTF_ELF_SEC, err); 1835 1529 goto out; 1836 1530 } 1837 - err = btf__finalize_data(obj, obj->btf); 1838 - if (err) { 1839 - pr_warn("Error finalizing %s: %d.\n", BTF_ELF_SEC, err); 1840 - goto out; 1841 - } 1842 1531 } 1843 1532 if (btf_ext_data) { 1844 1533 if (!obj->btf) { ··· 1863 1562 if (btf_required && !obj->btf) { 1864 1563 pr_warn("BTF is required, but is missing or corrupted.\n"); 1865 1564 return err == 0 ? -ENOENT : err; 1565 + } 1566 + return 0; 1567 + } 1568 + 1569 + static int bpf_object__finalize_btf(struct bpf_object *obj) 1570 + { 1571 + int err; 1572 + 1573 + if (!obj->btf) 1574 + return 0; 1575 + 1576 + err = btf__finalize_data(obj, obj->btf); 1577 + if (!err) 1578 + return 0; 1579 + 1580 + pr_warn("Error finalizing %s: %d.\n", BTF_ELF_SEC, err); 1581 + btf__free(obj->btf); 1582 + obj->btf = NULL; 1583 + btf_ext__free(obj->btf_ext); 1584 + obj->btf_ext = NULL; 1585 + 1586 + if (bpf_object__is_btf_mandatory(obj)) { 1587 + pr_warn("BTF is required, but is missing or corrupted.\n"); 1588 + return -ENOENT; 1866 1589 } 1867 1590 return 0; 1868 1591 } ··· 1991 1666 return -LIBBPF_ERRNO__FORMAT; 1992 1667 } 1993 1668 obj->efile.symbols = data; 1669 + obj->efile.symbols_shndx = idx; 1994 1670 obj->efile.strtabidx = sh.sh_link; 1995 1671 } else if (sh.sh_type == SHT_PROGBITS && data->d_size > 0) { 1996 1672 if (sh.sh_flags & SHF_EXECINSTR) { ··· 2010 1684 name, obj->path, cp); 2011 1685 return err; 2012 1686 } 2013 - } else if (strcmp(name, ".data") == 0) { 1687 + } else if (strcmp(name, DATA_SEC) == 0) { 2014 1688 obj->efile.data = data; 2015 1689 obj->efile.data_shndx = idx; 2016 - } else if (strcmp(name, ".rodata") == 0) { 1690 + } else if (strcmp(name, RODATA_SEC) == 0) { 2017 1691 obj->efile.rodata = data; 2018 1692 obj->efile.rodata_shndx = idx; 2019 1693 } else { ··· 2043 1717 2044 1718 obj->efile.reloc_sects[nr_sects].shdr = sh; 2045 1719 obj->efile.reloc_sects[nr_sects].data = data; 2046 - } else if (sh.sh_type == SHT_NOBITS && strcmp(name, ".bss") == 0) { 1720 + } else if (sh.sh_type == SHT_NOBITS && 1721 + strcmp(name, BSS_SEC) == 0) { 2047 1722 obj->efile.bss = data; 2048 1723 obj->efile.bss_shndx = idx; 2049 1724 } else { ··· 2057 1730 return -LIBBPF_ERRNO__FORMAT; 2058 1731 } 2059 1732 return bpf_object__init_btf(obj, btf_data, btf_ext_data); 1733 + } 1734 + 1735 + static bool sym_is_extern(const GElf_Sym *sym) 1736 + { 1737 + int bind = GELF_ST_BIND(sym->st_info); 1738 + /* externs are symbols w/ type=NOTYPE, bind=GLOBAL|WEAK, section=UND */ 1739 + return sym->st_shndx == SHN_UNDEF && 1740 + (bind == STB_GLOBAL || bind == STB_WEAK) && 1741 + GELF_ST_TYPE(sym->st_info) == STT_NOTYPE; 1742 + } 1743 + 1744 + static int find_extern_btf_id(const struct btf *btf, const char *ext_name) 1745 + { 1746 + const struct btf_type *t; 1747 + const char *var_name; 1748 + int i, n; 1749 + 1750 + if (!btf) 1751 + return -ESRCH; 1752 + 1753 + n = btf__get_nr_types(btf); 1754 + for (i = 1; i <= n; i++) { 1755 + t = btf__type_by_id(btf, i); 1756 + 1757 + if (!btf_is_var(t)) 1758 + continue; 1759 + 1760 + var_name = btf__name_by_offset(btf, t->name_off); 1761 + if (strcmp(var_name, ext_name)) 1762 + continue; 1763 + 1764 + if (btf_var(t)->linkage != BTF_VAR_GLOBAL_EXTERN) 1765 + return -EINVAL; 1766 + 1767 + return i; 1768 + } 1769 + 1770 + return -ENOENT; 1771 + } 1772 + 1773 + static enum extern_type find_extern_type(const struct btf *btf, int id, 1774 + bool *is_signed) 1775 + { 1776 + const struct btf_type *t; 1777 + const char *name; 1778 + 1779 + t = skip_mods_and_typedefs(btf, id, NULL); 1780 + name = btf__name_by_offset(btf, t->name_off); 1781 + 1782 + if (is_signed) 1783 + *is_signed = false; 1784 + switch (btf_kind(t)) { 1785 + case BTF_KIND_INT: { 1786 + int enc = btf_int_encoding(t); 1787 + 1788 + if (enc & BTF_INT_BOOL) 1789 + return t->size == 1 ? EXT_BOOL : EXT_UNKNOWN; 1790 + if (is_signed) 1791 + *is_signed = enc & BTF_INT_SIGNED; 1792 + if (t->size == 1) 1793 + return EXT_CHAR; 1794 + if (t->size < 1 || t->size > 8 || (t->size & (t->size - 1))) 1795 + return EXT_UNKNOWN; 1796 + return EXT_INT; 1797 + } 1798 + case BTF_KIND_ENUM: 1799 + if (t->size != 4) 1800 + return EXT_UNKNOWN; 1801 + if (strcmp(name, "libbpf_tristate")) 1802 + return EXT_UNKNOWN; 1803 + return EXT_TRISTATE; 1804 + case BTF_KIND_ARRAY: 1805 + if (btf_array(t)->nelems == 0) 1806 + return EXT_UNKNOWN; 1807 + if (find_extern_type(btf, btf_array(t)->type, NULL) != EXT_CHAR) 1808 + return EXT_UNKNOWN; 1809 + return EXT_CHAR_ARR; 1810 + default: 1811 + return EXT_UNKNOWN; 1812 + } 1813 + } 1814 + 1815 + static int cmp_externs(const void *_a, const void *_b) 1816 + { 1817 + const struct extern_desc *a = _a; 1818 + const struct extern_desc *b = _b; 1819 + 1820 + /* descending order by alignment requirements */ 1821 + if (a->align != b->align) 1822 + return a->align > b->align ? -1 : 1; 1823 + /* ascending order by size, within same alignment class */ 1824 + if (a->sz != b->sz) 1825 + return a->sz < b->sz ? -1 : 1; 1826 + /* resolve ties by name */ 1827 + return strcmp(a->name, b->name); 1828 + } 1829 + 1830 + static int bpf_object__collect_externs(struct bpf_object *obj) 1831 + { 1832 + const struct btf_type *t; 1833 + struct extern_desc *ext; 1834 + int i, n, off, btf_id; 1835 + struct btf_type *sec; 1836 + const char *ext_name; 1837 + Elf_Scn *scn; 1838 + GElf_Shdr sh; 1839 + 1840 + if (!obj->efile.symbols) 1841 + return 0; 1842 + 1843 + scn = elf_getscn(obj->efile.elf, obj->efile.symbols_shndx); 1844 + if (!scn) 1845 + return -LIBBPF_ERRNO__FORMAT; 1846 + if (gelf_getshdr(scn, &sh) != &sh) 1847 + return -LIBBPF_ERRNO__FORMAT; 1848 + n = sh.sh_size / sh.sh_entsize; 1849 + 1850 + pr_debug("looking for externs among %d symbols...\n", n); 1851 + for (i = 0; i < n; i++) { 1852 + GElf_Sym sym; 1853 + 1854 + if (!gelf_getsym(obj->efile.symbols, i, &sym)) 1855 + return -LIBBPF_ERRNO__FORMAT; 1856 + if (!sym_is_extern(&sym)) 1857 + continue; 1858 + ext_name = elf_strptr(obj->efile.elf, obj->efile.strtabidx, 1859 + sym.st_name); 1860 + if (!ext_name || !ext_name[0]) 1861 + continue; 1862 + 1863 + ext = obj->externs; 1864 + ext = reallocarray(ext, obj->nr_extern + 1, sizeof(*ext)); 1865 + if (!ext) 1866 + return -ENOMEM; 1867 + obj->externs = ext; 1868 + ext = &ext[obj->nr_extern]; 1869 + memset(ext, 0, sizeof(*ext)); 1870 + obj->nr_extern++; 1871 + 1872 + ext->btf_id = find_extern_btf_id(obj->btf, ext_name); 1873 + if (ext->btf_id <= 0) { 1874 + pr_warn("failed to find BTF for extern '%s': %d\n", 1875 + ext_name, ext->btf_id); 1876 + return ext->btf_id; 1877 + } 1878 + t = btf__type_by_id(obj->btf, ext->btf_id); 1879 + ext->name = btf__name_by_offset(obj->btf, t->name_off); 1880 + ext->sym_idx = i; 1881 + ext->is_weak = GELF_ST_BIND(sym.st_info) == STB_WEAK; 1882 + ext->sz = btf__resolve_size(obj->btf, t->type); 1883 + if (ext->sz <= 0) { 1884 + pr_warn("failed to resolve size of extern '%s': %d\n", 1885 + ext_name, ext->sz); 1886 + return ext->sz; 1887 + } 1888 + ext->align = btf__align_of(obj->btf, t->type); 1889 + if (ext->align <= 0) { 1890 + pr_warn("failed to determine alignment of extern '%s': %d\n", 1891 + ext_name, ext->align); 1892 + return -EINVAL; 1893 + } 1894 + ext->type = find_extern_type(obj->btf, t->type, 1895 + &ext->is_signed); 1896 + if (ext->type == EXT_UNKNOWN) { 1897 + pr_warn("extern '%s' type is unsupported\n", ext_name); 1898 + return -ENOTSUP; 1899 + } 1900 + } 1901 + pr_debug("collected %d externs total\n", obj->nr_extern); 1902 + 1903 + if (!obj->nr_extern) 1904 + return 0; 1905 + 1906 + /* sort externs by (alignment, size, name) and calculate their offsets 1907 + * within a map */ 1908 + qsort(obj->externs, obj->nr_extern, sizeof(*ext), cmp_externs); 1909 + off = 0; 1910 + for (i = 0; i < obj->nr_extern; i++) { 1911 + ext = &obj->externs[i]; 1912 + ext->data_off = roundup(off, ext->align); 1913 + off = ext->data_off + ext->sz; 1914 + pr_debug("extern #%d: symbol %d, off %u, name %s\n", 1915 + i, ext->sym_idx, ext->data_off, ext->name); 1916 + } 1917 + 1918 + btf_id = btf__find_by_name(obj->btf, EXTERN_SEC); 1919 + if (btf_id <= 0) { 1920 + pr_warn("no BTF info found for '%s' datasec\n", EXTERN_SEC); 1921 + return -ESRCH; 1922 + } 1923 + 1924 + sec = (struct btf_type *)btf__type_by_id(obj->btf, btf_id); 1925 + sec->size = off; 1926 + n = btf_vlen(sec); 1927 + for (i = 0; i < n; i++) { 1928 + struct btf_var_secinfo *vs = btf_var_secinfos(sec) + i; 1929 + 1930 + t = btf__type_by_id(obj->btf, vs->type); 1931 + ext_name = btf__name_by_offset(obj->btf, t->name_off); 1932 + ext = find_extern_by_name(obj, ext_name); 1933 + if (!ext) { 1934 + pr_warn("failed to find extern definition for BTF var '%s'\n", 1935 + ext_name); 1936 + return -ESRCH; 1937 + } 1938 + vs->offset = ext->data_off; 1939 + btf_var(t)->linkage = BTF_VAR_GLOBAL_ALLOCATED; 1940 + } 1941 + 1942 + return 0; 2060 1943 } 2061 1944 2062 1945 static struct bpf_program * ··· 2333 1796 return LIBBPF_MAP_BSS; 2334 1797 else if (shndx == obj->efile.rodata_shndx) 2335 1798 return LIBBPF_MAP_RODATA; 1799 + else if (shndx == obj->efile.symbols_shndx) 1800 + return LIBBPF_MAP_EXTERN; 2336 1801 else 2337 1802 return LIBBPF_MAP_UNSPEC; 2338 1803 } ··· 2379 1840 insn_idx, insn->code); 2380 1841 return -LIBBPF_ERRNO__RELOC; 2381 1842 } 1843 + 1844 + if (sym_is_extern(sym)) { 1845 + int sym_idx = GELF_R_SYM(rel->r_info); 1846 + int i, n = obj->nr_extern; 1847 + struct extern_desc *ext; 1848 + 1849 + for (i = 0; i < n; i++) { 1850 + ext = &obj->externs[i]; 1851 + if (ext->sym_idx == sym_idx) 1852 + break; 1853 + } 1854 + if (i >= n) { 1855 + pr_warn("extern relo failed to find extern for sym %d\n", 1856 + sym_idx); 1857 + return -LIBBPF_ERRNO__RELOC; 1858 + } 1859 + pr_debug("found extern #%d '%s' (sym %d, off %u) for insn %u\n", 1860 + i, ext->name, ext->sym_idx, ext->data_off, insn_idx); 1861 + reloc_desc->type = RELO_EXTERN; 1862 + reloc_desc->insn_idx = insn_idx; 1863 + reloc_desc->sym_off = ext->data_off; 1864 + return 0; 1865 + } 1866 + 2382 1867 if (!shdr_idx || shdr_idx >= SHN_LORESERVE) { 2383 1868 pr_warn("invalid relo for \'%s\' in special section 0x%x; forgot to initialize global var?..\n", 2384 1869 name, shdr_idx); ··· 2864 2301 static int 2865 2302 bpf_object__populate_internal_map(struct bpf_object *obj, struct bpf_map *map) 2866 2303 { 2304 + enum libbpf_map_type map_type = map->libbpf_type; 2867 2305 char *cp, errmsg[STRERR_BUFSIZE]; 2868 2306 int err, zero = 0; 2869 2307 2870 - /* Nothing to do here since kernel already zero-initializes .bss map. */ 2871 - if (map->libbpf_type == LIBBPF_MAP_BSS) 2308 + /* kernel already zero-initializes .bss map. */ 2309 + if (map_type == LIBBPF_MAP_BSS) 2872 2310 return 0; 2873 2311 2874 2312 err = bpf_map_update_elem(map->fd, &zero, map->mmaped, 0); ··· 2881 2317 return err; 2882 2318 } 2883 2319 2884 - /* Freeze .rodata map as read-only from syscall side. */ 2885 - if (map->libbpf_type == LIBBPF_MAP_RODATA) { 2320 + /* Freeze .rodata and .extern map as read-only from syscall side. */ 2321 + if (map_type == LIBBPF_MAP_RODATA || map_type == LIBBPF_MAP_EXTERN) { 2886 2322 err = bpf_map_freeze(map->fd); 2887 2323 if (err) { 2888 2324 err = -errno; ··· 4131 3567 size_t new_cnt; 4132 3568 int err; 4133 3569 4134 - if (relo->type != RELO_CALL) 4135 - return -LIBBPF_ERRNO__RELOC; 4136 - 4137 3570 if (prog->idx == obj->efile.text_shndx) { 4138 3571 pr_warn("relo in .text insn %d into off %d (insn #%d)\n", 4139 3572 relo->insn_idx, relo->sym_off, relo->sym_off / 8); ··· 4192 3631 4193 3632 for (i = 0; i < prog->nr_reloc; i++) { 4194 3633 struct reloc_desc *relo = &prog->reloc_desc[i]; 3634 + struct bpf_insn *insn = &prog->insns[relo->insn_idx]; 4195 3635 4196 - if (relo->type == RELO_LD64 || relo->type == RELO_DATA) { 4197 - struct bpf_insn *insn = &prog->insns[relo->insn_idx]; 3636 + if (relo->insn_idx + 1 >= (int)prog->insns_cnt) { 3637 + pr_warn("relocation out of range: '%s'\n", 3638 + prog->section_name); 3639 + return -LIBBPF_ERRNO__RELOC; 3640 + } 4198 3641 4199 - if (relo->insn_idx + 1 >= (int)prog->insns_cnt) { 4200 - pr_warn("relocation out of range: '%s'\n", 4201 - prog->section_name); 4202 - return -LIBBPF_ERRNO__RELOC; 4203 - } 4204 - 4205 - if (relo->type != RELO_DATA) { 4206 - insn[0].src_reg = BPF_PSEUDO_MAP_FD; 4207 - } else { 4208 - insn[0].src_reg = BPF_PSEUDO_MAP_VALUE; 4209 - insn[1].imm = insn[0].imm + relo->sym_off; 4210 - } 3642 + switch (relo->type) { 3643 + case RELO_LD64: 3644 + insn[0].src_reg = BPF_PSEUDO_MAP_FD; 4211 3645 insn[0].imm = obj->maps[relo->map_idx].fd; 4212 - } else if (relo->type == RELO_CALL) { 3646 + break; 3647 + case RELO_DATA: 3648 + insn[0].src_reg = BPF_PSEUDO_MAP_VALUE; 3649 + insn[1].imm = insn[0].imm + relo->sym_off; 3650 + insn[0].imm = obj->maps[relo->map_idx].fd; 3651 + break; 3652 + case RELO_EXTERN: 3653 + insn[0].src_reg = BPF_PSEUDO_MAP_VALUE; 3654 + insn[0].imm = obj->maps[obj->extern_map_idx].fd; 3655 + insn[1].imm = relo->sym_off; 3656 + break; 3657 + case RELO_CALL: 4213 3658 err = bpf_program__reloc_text(prog, obj, relo); 4214 3659 if (err) 4215 3660 return err; 3661 + break; 3662 + default: 3663 + pr_warn("relo #%d: bad relo type %d\n", i, relo->type); 3664 + return -EINVAL; 4216 3665 } 4217 3666 } 4218 3667 ··· 4502 3931 __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz, 4503 3932 const struct bpf_object_open_opts *opts) 4504 3933 { 3934 + const char *obj_name, *kconfig_path; 4505 3935 struct bpf_program *prog; 4506 3936 struct bpf_object *obj; 4507 - const char *obj_name; 4508 3937 char tmp_name[64]; 4509 - __u32 attach_prog_fd; 4510 3938 int err; 4511 3939 4512 3940 if (elf_version(EV_CURRENT) == EV_NONE) { ··· 4534 3964 return obj; 4535 3965 4536 3966 obj->relaxed_core_relocs = OPTS_GET(opts, relaxed_core_relocs, false); 4537 - attach_prog_fd = OPTS_GET(opts, attach_prog_fd, 0); 3967 + kconfig_path = OPTS_GET(opts, kconfig_path, NULL); 3968 + if (kconfig_path) { 3969 + obj->kconfig_path = strdup(kconfig_path); 3970 + if (!obj->kconfig_path) 3971 + return ERR_PTR(-ENOMEM); 3972 + } 4538 3973 4539 3974 err = bpf_object__elf_init(obj); 4540 3975 err = err ? : bpf_object__check_endianness(obj); 4541 3976 err = err ? : bpf_object__elf_collect(obj); 3977 + err = err ? : bpf_object__collect_externs(obj); 3978 + err = err ? : bpf_object__finalize_btf(obj); 4542 3979 err = err ? : bpf_object__init_maps(obj, opts); 4543 3980 err = err ? : bpf_object__init_prog_names(obj); 4544 3981 err = err ? : bpf_object__collect_reloc(obj); ··· 4568 3991 bpf_program__set_type(prog, prog_type); 4569 3992 bpf_program__set_expected_attach_type(prog, attach_type); 4570 3993 if (prog_type == BPF_PROG_TYPE_TRACING) 4571 - prog->attach_prog_fd = attach_prog_fd; 3994 + prog->attach_prog_fd = OPTS_GET(opts, attach_prog_fd, 0); 4572 3995 } 4573 3996 4574 3997 return obj; ··· 4679 4102 return 0; 4680 4103 } 4681 4104 4105 + static int bpf_object__resolve_externs(struct bpf_object *obj, 4106 + const char *config_path) 4107 + { 4108 + bool need_config = false; 4109 + struct extern_desc *ext; 4110 + int err, i; 4111 + void *data; 4112 + 4113 + if (obj->nr_extern == 0) 4114 + return 0; 4115 + 4116 + data = obj->maps[obj->extern_map_idx].mmaped; 4117 + 4118 + for (i = 0; i < obj->nr_extern; i++) { 4119 + ext = &obj->externs[i]; 4120 + 4121 + if (strcmp(ext->name, "LINUX_KERNEL_VERSION") == 0) { 4122 + void *ext_val = data + ext->data_off; 4123 + __u32 kver = get_kernel_version(); 4124 + 4125 + if (!kver) { 4126 + pr_warn("failed to get kernel version\n"); 4127 + return -EINVAL; 4128 + } 4129 + err = set_ext_value_num(ext, ext_val, kver); 4130 + if (err) 4131 + return err; 4132 + pr_debug("extern %s=0x%x\n", ext->name, kver); 4133 + } else if (strncmp(ext->name, "CONFIG_", 7) == 0) { 4134 + need_config = true; 4135 + } else { 4136 + pr_warn("unrecognized extern '%s'\n", ext->name); 4137 + return -EINVAL; 4138 + } 4139 + } 4140 + if (need_config) { 4141 + err = bpf_object__read_kernel_config(obj, config_path, data); 4142 + if (err) 4143 + return -EINVAL; 4144 + } 4145 + for (i = 0; i < obj->nr_extern; i++) { 4146 + ext = &obj->externs[i]; 4147 + 4148 + if (!ext->is_set && !ext->is_weak) { 4149 + pr_warn("extern %s (strong) not resolved\n", ext->name); 4150 + return -ESRCH; 4151 + } else if (!ext->is_set) { 4152 + pr_debug("extern %s (weak) not resolved, defaulting to zero\n", 4153 + ext->name); 4154 + } 4155 + } 4156 + 4157 + return 0; 4158 + } 4159 + 4682 4160 int bpf_object__load_xattr(struct bpf_object_load_attr *attr) 4683 4161 { 4684 4162 struct bpf_object *obj; ··· 4753 4121 obj->loaded = true; 4754 4122 4755 4123 err = bpf_object__probe_caps(obj); 4124 + err = err ? : bpf_object__resolve_externs(obj, obj->kconfig_path); 4756 4125 err = err ? : bpf_object__sanitize_and_load_btf(obj); 4757 4126 err = err ? : bpf_object__sanitize_maps(obj); 4758 4127 err = err ? : bpf_object__create_maps(obj); ··· 5346 4713 zfree(&map->name); 5347 4714 zfree(&map->pin_path); 5348 4715 } 4716 + 4717 + zfree(&obj->kconfig_path); 4718 + zfree(&obj->externs); 4719 + obj->nr_extern = 0; 5349 4720 5350 4721 zfree(&obj->maps); 5351 4722 obj->nr_maps = 0; ··· 7472 6835 return -ESRCH; 7473 6836 } 7474 6837 7475 - if (mmaped) 6838 + /* externs shouldn't be pre-setup from user code */ 6839 + if (mmaped && (*map)->libbpf_type != LIBBPF_MAP_EXTERN) 7476 6840 *mmaped = (*map)->mmaped; 7477 6841 } 7478 6842 ··· 7506 6868 size_t mmap_sz = bpf_map_mmap_sz(map); 7507 6869 int prot, map_fd = bpf_map__fd(map); 7508 6870 void **mmaped = s->maps[i].mmaped; 7509 - void *remapped; 7510 6871 7511 6872 if (!mmaped) 7512 6873 continue; ··· 7530 6893 * as per normal clean up procedure, so we don't need to worry 7531 6894 * about it from skeleton's clean up perspective. 7532 6895 */ 7533 - remapped = mmap(*mmaped, mmap_sz, prot, MAP_SHARED | MAP_FIXED, 7534 - map_fd, 0); 7535 - if (remapped == MAP_FAILED) { 6896 + *mmaped = mmap(map->mmaped, mmap_sz, prot, 6897 + MAP_SHARED | MAP_FIXED, map_fd, 0); 6898 + if (*mmaped == MAP_FAILED) { 7536 6899 err = -errno; 7537 6900 *mmaped = NULL; 7538 6901 pr_warn("failed to re-mmap() map '%s': %d\n",
+11 -1
tools/lib/bpf/libbpf.h
··· 85 85 */ 86 86 const char *pin_root_path; 87 87 __u32 attach_prog_fd; 88 + /* kernel config file path override (for CONFIG_ externs); can point 89 + * to either uncompressed text file or .gz file 90 + */ 91 + const char *kconfig_path; 88 92 }; 89 - #define bpf_object_open_opts__last_field attach_prog_fd 93 + #define bpf_object_open_opts__last_field kconfig_path 90 94 91 95 LIBBPF_API struct bpf_object *bpf_object__open(const char *path); 92 96 LIBBPF_API struct bpf_object * ··· 672 668 LIBBPF_API int bpf_object__attach_skeleton(struct bpf_object_skeleton *s); 673 669 LIBBPF_API void bpf_object__detach_skeleton(struct bpf_object_skeleton *s); 674 670 LIBBPF_API void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s); 671 + 672 + enum libbpf_tristate { 673 + TRI_NO = 0, 674 + TRI_YES = 1, 675 + TRI_MODULE = 2, 676 + }; 675 677 676 678 #ifdef __cplusplus 677 679 } /* extern "C" */
+1 -1
tools/testing/selftests/bpf/Makefile
··· 24 24 -I$(GENDIR) -I$(TOOLSINCDIR) -I$(CURDIR) \ 25 25 -Dbpf_prog_load=bpf_prog_test_load \ 26 26 -Dbpf_load_program=bpf_test_load_program 27 - LDLIBS += -lcap -lelf -lrt -lpthread 27 + LDLIBS += -lcap -lelf -lz -lrt -lpthread 28 28 29 29 # Order correspond to 'make run_tests' order 30 30 TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \
+195
tools/testing/selftests/bpf/prog_tests/core_extern.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2019 Facebook */ 3 + 4 + #include <test_progs.h> 5 + #include <sys/mman.h> 6 + #include <sys/utsname.h> 7 + #include <linux/version.h> 8 + #include "test_core_extern.skel.h" 9 + 10 + static uint32_t get_kernel_version(void) 11 + { 12 + uint32_t major, minor, patch; 13 + struct utsname info; 14 + 15 + uname(&info); 16 + if (sscanf(info.release, "%u.%u.%u", &major, &minor, &patch) != 3) 17 + return 0; 18 + return KERNEL_VERSION(major, minor, patch); 19 + } 20 + 21 + #define CFG "CONFIG_BPF_SYSCALL=n\n" 22 + 23 + static struct test_case { 24 + const char *name; 25 + const char *cfg; 26 + const char *cfg_path; 27 + bool fails; 28 + struct test_core_extern__data data; 29 + } test_cases[] = { 30 + { .name = "default search path", .cfg_path = NULL, 31 + .data = { .bpf_syscall = true } }, 32 + { .name = "/proc/config.gz", .cfg_path = "/proc/config.gz", 33 + .data = { .bpf_syscall = true } }, 34 + { .name = "missing config", .fails = true, 35 + .cfg_path = "/proc/invalid-config.gz" }, 36 + { 37 + .name = "custom values", 38 + .cfg = "CONFIG_BPF_SYSCALL=y\n" 39 + "CONFIG_TRISTATE=m\n" 40 + "CONFIG_BOOL=y\n" 41 + "CONFIG_CHAR=100\n" 42 + "CONFIG_USHORT=30000\n" 43 + "CONFIG_INT=123456\n" 44 + "CONFIG_ULONG=0xDEADBEEFC0DE\n" 45 + "CONFIG_STR=\"abracad\"\n" 46 + "CONFIG_MISSING=0", 47 + .data = { 48 + .bpf_syscall = true, 49 + .tristate_val = TRI_MODULE, 50 + .bool_val = true, 51 + .char_val = 100, 52 + .ushort_val = 30000, 53 + .int_val = 123456, 54 + .ulong_val = 0xDEADBEEFC0DE, 55 + .str_val = "abracad", 56 + }, 57 + }, 58 + /* TRISTATE */ 59 + { .name = "tristate (y)", .cfg = CFG"CONFIG_TRISTATE=y\n", 60 + .data = { .tristate_val = TRI_YES } }, 61 + { .name = "tristate (n)", .cfg = CFG"CONFIG_TRISTATE=n\n", 62 + .data = { .tristate_val = TRI_NO } }, 63 + { .name = "tristate (m)", .cfg = CFG"CONFIG_TRISTATE=m\n", 64 + .data = { .tristate_val = TRI_MODULE } }, 65 + { .name = "tristate (int)", .fails = 1, .cfg = CFG"CONFIG_TRISTATE=1" }, 66 + { .name = "tristate (bad)", .fails = 1, .cfg = CFG"CONFIG_TRISTATE=M" }, 67 + /* BOOL */ 68 + { .name = "bool (y)", .cfg = CFG"CONFIG_BOOL=y\n", 69 + .data = { .bool_val = true } }, 70 + { .name = "bool (n)", .cfg = CFG"CONFIG_BOOL=n\n", 71 + .data = { .bool_val = false } }, 72 + { .name = "bool (tristate)", .fails = 1, .cfg = CFG"CONFIG_BOOL=m" }, 73 + { .name = "bool (int)", .fails = 1, .cfg = CFG"CONFIG_BOOL=1" }, 74 + /* CHAR */ 75 + { .name = "char (tristate)", .cfg = CFG"CONFIG_CHAR=m\n", 76 + .data = { .char_val = 'm' } }, 77 + { .name = "char (bad)", .fails = 1, .cfg = CFG"CONFIG_CHAR=q\n" }, 78 + { .name = "char (empty)", .fails = 1, .cfg = CFG"CONFIG_CHAR=\n" }, 79 + { .name = "char (str)", .fails = 1, .cfg = CFG"CONFIG_CHAR=\"y\"\n" }, 80 + /* STRING */ 81 + { .name = "str (empty)", .cfg = CFG"CONFIG_STR=\"\"\n", 82 + .data = { .str_val = "\0\0\0\0\0\0\0" } }, 83 + { .name = "str (padded)", .cfg = CFG"CONFIG_STR=\"abra\"\n", 84 + .data = { .str_val = "abra\0\0\0" } }, 85 + { .name = "str (too long)", .cfg = CFG"CONFIG_STR=\"abracada\"\n", 86 + .data = { .str_val = "abracad" } }, 87 + { .name = "str (no value)", .fails = 1, .cfg = CFG"CONFIG_STR=\n" }, 88 + { .name = "str (bad value)", .fails = 1, .cfg = CFG"CONFIG_STR=bla\n" }, 89 + /* INTEGERS */ 90 + { 91 + .name = "integer forms", 92 + .cfg = CFG 93 + "CONFIG_CHAR=0xA\n" 94 + "CONFIG_USHORT=0462\n" 95 + "CONFIG_INT=-100\n" 96 + "CONFIG_ULONG=+1000000000000", 97 + .data = { 98 + .char_val = 0xA, 99 + .ushort_val = 0462, 100 + .int_val = -100, 101 + .ulong_val = 1000000000000, 102 + }, 103 + }, 104 + { .name = "int (bad)", .fails = 1, .cfg = CFG"CONFIG_INT=abc" }, 105 + { .name = "int (str)", .fails = 1, .cfg = CFG"CONFIG_INT=\"abc\"" }, 106 + { .name = "int (empty)", .fails = 1, .cfg = CFG"CONFIG_INT=" }, 107 + { .name = "int (mixed)", .fails = 1, .cfg = CFG"CONFIG_INT=123abc" }, 108 + { .name = "int (max)", .cfg = CFG"CONFIG_INT=2147483647", 109 + .data = { .int_val = 2147483647 } }, 110 + { .name = "int (min)", .cfg = CFG"CONFIG_INT=-2147483648", 111 + .data = { .int_val = -2147483648 } }, 112 + { .name = "int (max+1)", .fails = 1, .cfg = CFG"CONFIG_INT=2147483648" }, 113 + { .name = "int (min-1)", .fails = 1, .cfg = CFG"CONFIG_INT=-2147483649" }, 114 + { .name = "ushort (max)", .cfg = CFG"CONFIG_USHORT=65535", 115 + .data = { .ushort_val = 65535 } }, 116 + { .name = "ushort (min)", .cfg = CFG"CONFIG_USHORT=0", 117 + .data = { .ushort_val = 0 } }, 118 + { .name = "ushort (max+1)", .fails = 1, .cfg = CFG"CONFIG_USHORT=65536" }, 119 + { .name = "ushort (min-1)", .fails = 1, .cfg = CFG"CONFIG_USHORT=-1" }, 120 + { .name = "u64 (max)", .cfg = CFG"CONFIG_ULONG=0xffffffffffffffff", 121 + .data = { .ulong_val = 0xffffffffffffffff } }, 122 + { .name = "u64 (min)", .cfg = CFG"CONFIG_ULONG=0", 123 + .data = { .ulong_val = 0 } }, 124 + { .name = "u64 (max+1)", .fails = 1, .cfg = CFG"CONFIG_ULONG=0x10000000000000000" }, 125 + }; 126 + 127 + BPF_EMBED_OBJ(core_extern, "test_core_extern.o"); 128 + 129 + void test_core_extern(void) 130 + { 131 + const uint32_t kern_ver = get_kernel_version(); 132 + int err, duration = 0, i, j; 133 + struct test_core_extern *skel = NULL; 134 + uint64_t *got, *exp; 135 + int n = sizeof(*skel->data) / sizeof(uint64_t); 136 + 137 + for (i = 0; i < ARRAY_SIZE(test_cases); i++) { 138 + char tmp_cfg_path[] = "/tmp/test_core_extern_cfg.XXXXXX"; 139 + struct test_case *t = &test_cases[i]; 140 + DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts, 141 + .kconfig_path = t->cfg_path, 142 + ); 143 + 144 + if (!test__start_subtest(t->name)) 145 + continue; 146 + 147 + if (t->cfg) { 148 + size_t n = strlen(t->cfg) + 1; 149 + int fd = mkstemp(tmp_cfg_path); 150 + int written; 151 + 152 + if (CHECK(fd < 0, "mkstemp", "errno: %d\n", errno)) 153 + continue; 154 + printf("using '%s' as config file\n", tmp_cfg_path); 155 + written = write(fd, t->cfg, n); 156 + close(fd); 157 + if (CHECK_FAIL(written != n)) 158 + goto cleanup; 159 + opts.kconfig_path = tmp_cfg_path; 160 + } 161 + 162 + skel = test_core_extern__open_opts(&core_extern_embed, &opts); 163 + if (CHECK(!skel, "skel_open", "skeleton open failed\n")) 164 + goto cleanup; 165 + err = test_core_extern__load(skel); 166 + if (t->fails) { 167 + CHECK(!err, "skel_load", 168 + "shouldn't succeed open/load of skeleton\n"); 169 + goto cleanup; 170 + } else if (CHECK(err, "skel_load", 171 + "failed to open/load skeleton\n")) { 172 + goto cleanup; 173 + } 174 + err = test_core_extern__attach(skel); 175 + if (CHECK(err, "attach_raw_tp", "failed attach: %d\n", err)) 176 + goto cleanup; 177 + 178 + usleep(1); 179 + 180 + t->data.kern_ver = kern_ver; 181 + t->data.missing_val = 0xDEADC0DE; 182 + got = (uint64_t *)skel->data; 183 + exp = (uint64_t *)&t->data; 184 + for (j = 0; j < n; j++) { 185 + CHECK(got[j] != exp[j], "check_res", 186 + "result #%d: expected %lx, but got %lx\n", 187 + j, exp[j], got[j]); 188 + } 189 + cleanup: 190 + if (t->cfg) 191 + unlink(tmp_cfg_path); 192 + test_core_extern__destroy(skel); 193 + skel = NULL; 194 + } 195 + }
+17 -1
tools/testing/selftests/bpf/prog_tests/skeleton.c
··· 17 17 int duration = 0, err; 18 18 struct test_skeleton* skel; 19 19 struct test_skeleton__bss *bss; 20 + struct test_skeleton__externs *exts; 20 21 21 - skel = test_skeleton__open_and_load(&skeleton_embed); 22 + skel = test_skeleton__open(&skeleton_embed); 22 23 if (CHECK(!skel, "skel_open", "failed to open skeleton\n")) 23 24 return; 25 + 26 + printf("EXTERNS BEFORE: %p\n", skel->externs); 27 + if (CHECK(skel->externs, "skel_externs", "externs are mmaped()!\n")) 28 + goto cleanup; 29 + 30 + err = test_skeleton__load(skel); 31 + if (CHECK(err, "skel_load", "failed to load skeleton: %d\n", err)) 32 + goto cleanup; 33 + printf("EXTERNS AFTER: %p\n", skel->externs); 24 34 25 35 bss = skel->bss; 26 36 bss->in1 = 1; ··· 39 29 bss->in4 = 4; 40 30 bss->in5.a = 5; 41 31 bss->in5.b = 6; 32 + exts = skel->externs; 42 33 43 34 err = test_skeleton__attach(skel); 44 35 if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err)) ··· 56 45 bss->handler_out5.a, 5); 57 46 CHECK(bss->handler_out5.b != 6, "res6", "got %lld != exp %d\n", 58 47 bss->handler_out5.b, 6); 48 + 49 + CHECK(bss->bpf_syscall != exts->CONFIG_BPF_SYSCALL, "ext1", 50 + "got %d != exp %d\n", bss->bpf_syscall, exts->CONFIG_BPF_SYSCALL); 51 + CHECK(bss->kern_ver != exts->LINUX_KERNEL_VERSION, "ext2", 52 + "got %d != exp %d\n", bss->kern_ver, exts->LINUX_KERNEL_VERSION); 59 53 60 54 cleanup: 61 55 test_skeleton__destroy(skel);
+62
tools/testing/selftests/bpf/progs/test_core_extern.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2019 Facebook */ 3 + 4 + #include <stdint.h> 5 + #include <stdbool.h> 6 + #include <linux/ptrace.h> 7 + #include <linux/bpf.h> 8 + #include "bpf_helpers.h" 9 + 10 + /* non-existing BPF helper, to test dead code elimination */ 11 + static int (*bpf_missing_helper)(const void *arg1, int arg2) = (void *) 999; 12 + 13 + extern int LINUX_KERNEL_VERSION; 14 + extern bool CONFIG_BPF_SYSCALL; /* strong */ 15 + extern enum libbpf_tristate CONFIG_TRISTATE __weak; 16 + extern bool CONFIG_BOOL __weak; 17 + extern char CONFIG_CHAR __weak; 18 + extern uint16_t CONFIG_USHORT __weak; 19 + extern int CONFIG_INT __weak; 20 + extern uint64_t CONFIG_ULONG __weak; 21 + extern const char CONFIG_STR[8] __weak; 22 + extern uint64_t CONFIG_MISSING __weak; 23 + 24 + uint64_t kern_ver = -1; 25 + uint64_t bpf_syscall = -1; 26 + uint64_t tristate_val = -1; 27 + uint64_t bool_val = -1; 28 + uint64_t char_val = -1; 29 + uint64_t ushort_val = -1; 30 + uint64_t int_val = -1; 31 + uint64_t ulong_val = -1; 32 + char str_val[8] = {-1, -1, -1, -1, -1, -1, -1, -1}; 33 + uint64_t missing_val = -1; 34 + 35 + SEC("raw_tp/sys_enter") 36 + int handle_sys_enter(struct pt_regs *ctx) 37 + { 38 + int i; 39 + 40 + kern_ver = LINUX_KERNEL_VERSION; 41 + bpf_syscall = CONFIG_BPF_SYSCALL; 42 + tristate_val = CONFIG_TRISTATE; 43 + bool_val = CONFIG_BOOL; 44 + char_val = CONFIG_CHAR; 45 + ushort_val = CONFIG_USHORT; 46 + int_val = CONFIG_INT; 47 + ulong_val = CONFIG_ULONG; 48 + 49 + for (i = 0; i < sizeof(CONFIG_STR); i++) { 50 + str_val[i] = CONFIG_STR[i]; 51 + } 52 + 53 + if (CONFIG_MISSING) 54 + /* invalid, but dead code - never executed */ 55 + missing_val = bpf_missing_helper(ctx, 123); 56 + else 57 + missing_val = 0xDEADC0DE; 58 + 59 + return 0; 60 + } 61 + 62 + char _license[] SEC("license") = "GPL";
+9
tools/testing/selftests/bpf/progs/test_skeleton.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 /* Copyright (c) 2019 Facebook */ 3 3 4 + #include <stdbool.h> 4 5 #include <linux/bpf.h> 5 6 #include "bpf_helpers.h" 6 7 ··· 21 20 long long out4 = 0; 22 21 int out1 = 0; 23 22 23 + extern bool CONFIG_BPF_SYSCALL; 24 + extern int LINUX_KERNEL_VERSION; 25 + bool bpf_syscall = 0; 26 + int kern_ver = 0; 24 27 25 28 SEC("raw_tp/sys_enter") 26 29 int handler(const void *ctx) ··· 36 31 out3 = in3; 37 32 out4 = in4; 38 33 out5 = in5; 34 + 35 + bpf_syscall = CONFIG_BPF_SYSCALL; 36 + kern_ver = LINUX_KERNEL_VERSION; 37 + 39 38 return 0; 40 39 } 41 40