From b3d3c2ae577f41b1d6e656d1fc55dd3df158d033 Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Tue, 18 Mar 2025 15:06:34 +0900 Subject: [PATCH 1/5] Add method to update btf_type.size for BTF_KIND_DATASEC size in btf_type of BTF_KIND_DATASEC is 0 at compilation time. So bpf loader needs to update to actual size based on ELF's section size. Bug: 403381101 Test: boot Change-Id: I78fb26a5b8eeb120044b5737667f472fac51d79f --- bpf/loader/NetBpfLoad.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/bpf/loader/NetBpfLoad.cpp b/bpf/loader/NetBpfLoad.cpp index f8691fd06b..64ebe0c577 100644 --- a/bpf/loader/NetBpfLoad.cpp +++ b/bpf/loader/NetBpfLoad.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "NetBpfLoad" #include +#include #include #include #include @@ -649,6 +650,24 @@ static bool mapMatchesExpectations(const unique_fd& fd, const string& mapName, return false; } +static int setBtfDatasecSize(ifstream &elfFile, struct btf *btf, + struct btf_type *bt) { + const char *name = btf__name_by_offset(btf, bt->name_off); + if (!name) { + ALOGE("Couldn't resolve section name, errno: %d", errno); + return -errno; + } + + vector data; + int ret = readSectionByName(name, elfFile, data); + if (ret) { + ALOGE("Couldn't read section %s, ret: %d", name, ret); + return ret; + } + bt->size = data.size(); + return 0; +} + static int createMaps(const char* elfPath, ifstream& elfFile, vector& mapFds, const char* prefix, const unsigned int bpfloader_ver) { int ret; From efe33319f7129cf33b6036377feeb8c051c271ee Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Tue, 18 Mar 2025 15:08:15 +0900 Subject: [PATCH 2/5] Add method to update btf_var_secinfo.offset Bpf loader needs to fix btf_var_secinfo.offset based on ELF's string section. Bug: 403381101 Test: boot Change-Id: I8382d0ee3e0fa7f0a3b825eca9cdcf40ccce9532 --- bpf/loader/NetBpfLoad.cpp | 55 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/bpf/loader/NetBpfLoad.cpp b/bpf/loader/NetBpfLoad.cpp index 64ebe0c577..2d0e4c682f 100644 --- a/bpf/loader/NetBpfLoad.cpp +++ b/bpf/loader/NetBpfLoad.cpp @@ -668,6 +668,61 @@ static int setBtfDatasecSize(ifstream &elfFile, struct btf *btf, return 0; } +static int getSymOffsetByName(ifstream &elfFile, const char *name, int *off) { + vector symtab; + int ret = readSymTab(elfFile, 1 /* sort */, symtab); + if (ret) return ret; + for (int i = 0; i < (int)symtab.size(); i++) { + string s; + ret = getSymName(elfFile, symtab[i].st_name, s); + if (ret) continue; + if (!strcmp(s.c_str(), name)) { + *off = symtab[i].st_value; + return 0; + } + } + return -1; +} + +static int setBtfVarOffset(ifstream &elfFile, struct btf *btf, + struct btf_type *datasecBt) { + int i, vars = btf_vlen(datasecBt); + struct btf_var_secinfo *vsi; + const char *datasecName = btf__name_by_offset(btf, datasecBt->name_off); + if (!datasecName) { + ALOGE("Couldn't resolve section name, errno: %d", errno); + return -errno; + } + + for (i = 0, vsi = btf_var_secinfos(datasecBt); i < vars; i++, vsi++) { + const struct btf_type *varBt = btf__type_by_id(btf, vsi->type); + if (!varBt || !btf_is_var(varBt)) { + ALOGE("Found non VAR kind btf_type, section: %s id: %d", datasecName, + vsi->type); + return -1; + } + + const struct btf_var *var = btf_var(varBt); + if (var->linkage == BTF_VAR_STATIC) continue; + + const char *varName = btf__name_by_offset(btf, varBt->name_off); + if (!varName) { + ALOGE("Failed to resolve var name, section: %s", datasecName); + return -1; + } + + int off; + int ret = getSymOffsetByName(elfFile, varName, &off); + if (ret) { + ALOGE("No offset found in symbol table, section: %s, var: %s, ret: %d", + datasecName, varName, ret); + return ret; + } + vsi->offset = off; + } + return 0; +} + static int createMaps(const char* elfPath, ifstream& elfFile, vector& mapFds, const char* prefix, const unsigned int bpfloader_ver) { int ret; From 21536e55fc8f4e0f99989c1f83ca33c72abf3edb Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Tue, 18 Mar 2025 15:09:27 +0900 Subject: [PATCH 3/5] Add method to load btf into kernel Bug: 403381101 Test: boot Change-Id: I9d5aedc13e7f4551eaa97b855496148b573c2176 --- bpf/loader/NetBpfLoad.cpp | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/bpf/loader/NetBpfLoad.cpp b/bpf/loader/NetBpfLoad.cpp index 2d0e4c682f..5e01ef7d07 100644 --- a/bpf/loader/NetBpfLoad.cpp +++ b/bpf/loader/NetBpfLoad.cpp @@ -723,6 +723,43 @@ static int setBtfVarOffset(ifstream &elfFile, struct btf *btf, return 0; } +static int loadBtf(ifstream &elfFile, struct btf *btf) { + int ret; + for (unsigned int i = 1; i < btf__type_cnt(btf); ++i) { + struct btf_type *bt = (struct btf_type *)btf__type_by_id(btf, i); + if (!btf_is_datasec(bt)) continue; + ret = setBtfDatasecSize(elfFile, btf, bt); + if (ret) return ret; + ret = setBtfVarOffset(elfFile, btf, bt); + if (ret) return ret; + } + + ret = btf__load_into_kernel(btf); + if (ret) { + if (errno != EINVAL) { + ALOGE("btf__load_into_kernel failed, errno: %d", errno); + return ret; + }; + // For BTF_KIND_FUNC, newer kernels can read the BTF_INFO_VLEN bits of + // struct btf_type to distinguish static vs. global vs. extern + // functions, but older kernels enforce that only the BTF_INFO_KIND bits + // can be set. Retry with non-BTF_INFO_KIND bits zeroed out to handle + // this case. + for (unsigned int i = 1; i < btf__type_cnt(btf); ++i) { + struct btf_type *bt = (struct btf_type *)btf__type_by_id(btf, i); + if (btf_is_func(bt)) { + bt->info = (BTF_INFO_KIND(bt->info)) << 24; + } + } + ret = btf__load_into_kernel(btf); + if (ret) { + ALOGE("btf__load_into_kernel retry failed, errno: %d", errno); + return ret; + }; + } + return 0; +} + static int createMaps(const char* elfPath, ifstream& elfFile, vector& mapFds, const char* prefix, const unsigned int bpfloader_ver) { int ret; From 59b2099360b41e0e1f0a1331d7b9e59452f0f4bf Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Tue, 18 Mar 2025 15:10:19 +0900 Subject: [PATCH 4/5] Add method to get key value type ids BPF_ANNOTATE_KV_PAIR is used to tell the bpf loader about the btf key_type and value_type of a bpf map. This CL adds a method to get key value type ids from BPF_ANNOTATE_KV_PAIR Bug: 403381101 Test: boot Change-Id: If308d49b64b38f65991a0a8177e5072d54e2cdf6 --- bpf/loader/NetBpfLoad.cpp | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/bpf/loader/NetBpfLoad.cpp b/bpf/loader/NetBpfLoad.cpp index 5e01ef7d07..be4ed1f560 100644 --- a/bpf/loader/NetBpfLoad.cpp +++ b/bpf/loader/NetBpfLoad.cpp @@ -760,6 +760,67 @@ static int loadBtf(ifstream &elfFile, struct btf *btf) { return 0; } +int getKeyValueTids(const struct btf *btf, const char *mapName, + uint32_t expectedKeySize, uint32_t expectedValueSize, + uint32_t *keyTypeId, uint32_t *valueTypeId) { + const struct btf_type *kvBt; + const struct btf_member *key, *value; + const size_t max_name = 256; + char kvTypeName[max_name]; + int64_t keySize, valueSize; + uint32_t kvId; + + if (snprintf(kvTypeName, max_name, "____btf_map_%s", mapName) == max_name) { + ALOGE("____btf_map_%s is too long", mapName); + return -1; + } + + kvId = btf__find_by_name(btf, kvTypeName); + if (kvId < 0) { + ALOGE("section not found, map: %s typeName: %s", mapName, kvTypeName); + return -1; + } + + kvBt = btf__type_by_id(btf, kvId); + if (!kvBt) { + ALOGE("Couldn't find BTF type, map: %s id: %u", mapName, kvId); + return -1; + } + + if (!btf_is_struct(kvBt) || btf_vlen(kvBt) < 2) { + ALOGE("Non Struct kind or invalid vlen, map: %s id: %u", mapName, kvId); + return -1; + } + + key = btf_members(kvBt); + value = key + 1; + + keySize = btf__resolve_size(btf, key->type); + if (keySize < 0) { + ALOGE("Couldn't get key size, map: %s errno: %d", mapName, errno); + return -1; + } + + valueSize = btf__resolve_size(btf, value->type); + if (valueSize < 0) { + ALOGE("Couldn't get value size, map: %s errno: %d", mapName, errno); + return -1; + } + + if (expectedKeySize != keySize || expectedValueSize != valueSize) { + ALOGE("Key value size mismatch, map: %s key size: %d expected key size: " + "%d value size: %d expected value size: %d", + mapName, (uint32_t)keySize, expectedKeySize, (uint32_t)valueSize, + expectedValueSize); + return -1; + } + + *keyTypeId = key->type; + *valueTypeId = value->type; + + return 0; +} + static int createMaps(const char* elfPath, ifstream& elfFile, vector& mapFds, const char* prefix, const unsigned int bpfloader_ver) { int ret; From 1a5cc5b57c83d56ca0f419bdb5cf39dd1eeac615 Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Tue, 18 Mar 2025 15:25:13 +0900 Subject: [PATCH 5/5] Enable BTF for maps loaded by NetBpfLoad Bug: 403381101 Test: boot Change-Id: I05194e5a2678de07e3542aa744d2350a6d604ea2 --- bpf/loader/NetBpfLoad.cpp | 40 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/bpf/loader/NetBpfLoad.cpp b/bpf/loader/NetBpfLoad.cpp index be4ed1f560..28cb09e3e7 100644 --- a/bpf/loader/NetBpfLoad.cpp +++ b/bpf/loader/NetBpfLoad.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -821,10 +822,14 @@ int getKeyValueTids(const struct btf *btf, const char *mapName, return 0; } +static bool isBtfSupported(enum bpf_map_type type) { + return type != BPF_MAP_TYPE_DEVMAP_HASH && type != BPF_MAP_TYPE_RINGBUF; +} + static int createMaps(const char* elfPath, ifstream& elfFile, vector& mapFds, const char* prefix, const unsigned int bpfloader_ver) { int ret; - vector mdData; + vector mdData, btfData; vector md; vector mapNames; string objName = pathToObjName(string(elfPath)); @@ -851,6 +856,21 @@ static int createMaps(const char* elfPath, ifstream& elfFile, vector& ret = getSectionSymNames(elfFile, "maps", mapNames); if (ret) return ret; + ret = readSectionByName(".BTF", elfFile, btfData); + if (ret) { + ALOGE("Failed to read .BTF section, ret:%d", ret); + return ret; + } + struct btf *btf = btf__new(btfData.data(), btfData.size()); + if (btf == NULL) { + ALOGE("btf__new failed, errno: %d", errno); + return -errno; + } + auto scopeGuard = base::make_scope_guard([btf] { btf__free(btf); }); + + ret = loadBtf(elfFile, btf); + if (ret) return ret; + unsigned kvers = kernelVersion(); for (int i = 0; i < (int)mapNames.size(); i++) { @@ -976,12 +996,26 @@ static int createMaps(const char* elfPath, ifstream& elfFile, vector& }; if (isAtLeastKernelVersion(4, 15, 0)) strlcpy(req.map_name, mapNames[i].c_str(), sizeof(req.map_name)); + + if (isBtfSupported(type)) { + uint32_t kTid, vTid; + ret = getKeyValueTids(btf, mapNames[i].c_str(), md[i].key_size, + md[i].value_size, &kTid, &vTid); + if (ret) return ret; + req.btf_fd = btf__fd(btf); + req.btf_key_type_id = kTid; + req.btf_value_type_id = vTid; + ALOGI("Create map with BTF, map: %s", mapNames[i].c_str()); + } else { + ALOGI("Create map without BTF, map: %s", mapNames[i].c_str()); + } + fd.reset(bpf(BPF_MAP_CREATE, req)); saved_errno = errno; if (fd.ok()) { - ALOGD("bpf_create_map[%s] -> %d", mapNames[i].c_str(), fd.get()); + ALOGD("bpf_create_map[%s] -> %d", mapNames[i].c_str(), fd.get()); } else { - ALOGE("bpf_create_map[%s] -> %d errno:%d", mapNames[i].c_str(), fd.get(), saved_errno); + ALOGE("bpf_create_map[%s] -> %d errno:%d", mapNames[i].c_str(), fd.get(), saved_errno); } }