diff options
author | Connor O'Brien <connoro@google.com> | 2022-01-18 21:41:16 -0800 |
---|---|---|
committer | Connor O'Brien <connoro@google.com> | 2022-01-24 21:24:26 +0000 |
commit | 35425e5a884342b6b3ce50d990080288e3b3140b (patch) | |
tree | 1addf60d269a6bff12b5c8b79e104adf288dfc61 /libbpf_android | |
parent | 00fa9635f8ccf36c8ae57b113e80d6c19c5c19c3 (diff) | |
download | bpf-35425e5a884342b6b3ce50d990080288e3b3140b.tar.gz |
bpfloader: load map BTF via btfloader
btfloader uses upstream libbpf & libelf to parse the BTF sections of a
bpf .o file, load BTF information into the kernel, and identify the
BTF type ids corresponding to each map's keys and values.
Extend bpfloader to run btfloader on any .o file with a .BTF
section. We pass btfloader socket and pipe fds, which it will use to
send back a BTF fd and the key & value type ids, respectively.
To ensure a btfloader bug can't block bpfloader indefinitely, wait for
btfloader to run with a short timeout and use nonblocking socket &
pipe for communication.
Finally, add a check to BpfLoadTest to verify that map BTF is loaded
as expected.
Test: build & boot; existing programs load successfully
Test: libbpf_load_test
Signed-off-by: Connor O'Brien <connoro@google.com>
Change-Id: I7bac83a08c2dc452bdb9030f1e74781116c1dd5c
Diffstat (limited to 'libbpf_android')
-rw-r--r-- | libbpf_android/BpfLoadTest.cpp | 15 | ||||
-rw-r--r-- | libbpf_android/Loader.cpp | 106 |
2 files changed, 118 insertions, 3 deletions
diff --git a/libbpf_android/BpfLoadTest.cpp b/libbpf_android/BpfLoadTest.cpp index 2560890..72c0fc1 100644 --- a/libbpf_android/BpfLoadTest.cpp +++ b/libbpf_android/BpfLoadTest.cpp @@ -85,6 +85,17 @@ class BpfLoadTest : public TestWithParam<std::string> { EXPECT_RESULT_OK(m.iterateWithValue(iterFunc)); EXPECT_EQ(non_zero, 1); } + + void checkMapBtf() { + // Earlier kernels lack BPF_BTF_LOAD support + if (!isAtLeastKernelVersion(4, 19, 0)) GTEST_SKIP() << "pre-4.19 kernel does not support BTF"; + + const bool haveBtf = GetParam().find("btf") != std::string::npos; + + std::string str; + EXPECT_EQ(android::base::ReadFileToString(mTpMapPath, &str), haveBtf); + if (haveBtf) EXPECT_FALSE(str.empty()); + } }; INSTANTIATE_TEST_SUITE_P(BpfLoadTests, BpfLoadTest, @@ -94,5 +105,9 @@ TEST_P(BpfLoadTest, bpfCheckMap) { checkMapNonZero(); } +TEST_P(BpfLoadTest, bpfCheckBtf) { + checkMapBtf(); +} + } // namespace bpf } // namespace android diff --git a/libbpf_android/Loader.cpp b/libbpf_android/Loader.cpp index cd799e4..b10cfe7 100644 --- a/libbpf_android/Loader.cpp +++ b/libbpf_android/Loader.cpp @@ -24,8 +24,10 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sysexits.h> #include <sys/stat.h> #include <sys/utsname.h> +#include <sys/wait.h> #include <unistd.h> // This is BpfLoader v0.8 @@ -37,13 +39,18 @@ #include "bpf/bpf_map_def.h" #include "include/libbpf_android.h" +#include <bpf/bpf.h> + #include <cstdlib> #include <fstream> #include <iostream> #include <optional> #include <string> +#include <unordered_map> #include <vector> +#include <android-base/cmsg.h> +#include <android-base/file.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> @@ -472,12 +479,88 @@ static int getSymNameByIdx(ifstream& elfFile, int index, string& name) { return getSymName(elfFile, symtab[index].st_name, name); } +static bool waitpidTimeout(pid_t pid, int timeoutMs) { + // Add SIGCHLD to the signal set. + sigset_t child_mask, original_mask; + sigemptyset(&child_mask); + sigaddset(&child_mask, SIGCHLD); + if (sigprocmask(SIG_BLOCK, &child_mask, &original_mask) == -1) return false; + + // Wait for a SIGCHLD notification. + errno = 0; + timespec ts = {0, timeoutMs * 1000000}; + int wait_result = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, nullptr, &ts)); + + // Restore the original signal set. + sigprocmask(SIG_SETMASK, &original_mask, nullptr); + + if (wait_result == -1) return false; + + int status; + return TEMP_FAILURE_RETRY(waitpid(pid, &status, WNOHANG)) == pid; +} + +static std::optional<unique_fd> getMapBtfInfo(const char* elfPath, + std::unordered_map<string, std::pair<uint32_t, uint32_t>> &btfTypeIds) { + unique_fd bpfloaderSocket, btfloaderSocket; + if (!android::base::Socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, &bpfloaderSocket, + &btfloaderSocket)) { + return {}; + } + + unique_fd pipeRead, pipeWrite; + if (!android::base::Pipe(&pipeRead, &pipeWrite, O_NONBLOCK)) { + return {}; + } + + pid_t pid = fork(); + if (pid < 0) return {}; + if (!pid) { + bpfloaderSocket.reset(); + pipeRead.reset(); + auto socketFdStr = std::to_string(btfloaderSocket.release()); + auto pipeFdStr = std::to_string(pipeWrite.release()); + + if (execl("/system/bin/btfloader", "/system/bin/btfloader", socketFdStr.c_str(), + pipeFdStr.c_str(), elfPath, NULL) == -1) { + ALOGW("exec btfloader failed with errno %d (%s)\n", errno, strerror(errno)); + exit(EX_UNAVAILABLE); + } + } + btfloaderSocket.reset(); + pipeWrite.reset(); + if (!waitpidTimeout(pid, 100)) { + kill(pid, SIGKILL); + return {}; + } + + unique_fd btfFd; + if (android::base::ReceiveFileDescriptors(bpfloaderSocket, nullptr, 0, &btfFd)) return {}; + + std::string btfTypeIdStr; + if (!android::base::ReadFdToString(pipeRead, &btfTypeIdStr)) return {}; + if (btfFd.get() < 0) return {}; + + const auto mapTypeIdLines = android::base::Split(btfTypeIdStr, "\n"); + for (const auto &line : mapTypeIdLines) { + const auto vec = android::base::Split(line, " "); + // Splitting on newline will give us one empty line + if (vec.size() != 3) continue; + const int kTid = atoi(vec[1].c_str()); + const int vTid = atoi(vec[2].c_str()); + if (!kTid || !vTid) return {}; + btfTypeIds[vec[0]] = std::make_pair(kTid, vTid); + } + return btfFd; +} + static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& mapFds, const char* prefix, size_t sizeOfBpfMapDef) { int ret; - vector<char> mdData; + vector<char> mdData, btfData; vector<struct bpf_map_def> md; vector<string> mapNames; + std::unordered_map<string, std::pair<uint32_t, uint32_t>> btfTypeIdMap; string fname = pathToFilename(string(elfPath), true); ret = readSectionByName("maps", elfFile, mdData); @@ -510,6 +593,11 @@ static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& ret = getSectionSymNames(elfFile, "maps", mapNames); if (ret) return ret; + std::optional<unique_fd> btfFd; + if (!readSectionByName(".BTF", elfFile, btfData)) { + btfFd = getMapBtfInfo(elfPath, btfTypeIdMap); + } + unsigned kvers = kernelVersion(); for (int i = 0; i < (int)mapNames.size(); i++) { @@ -575,8 +663,20 @@ static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& // programs as being 5.4+... type = BPF_MAP_TYPE_HASH; } - fd.reset(bcc_create_map(type, mapNames[i].c_str(), md[i].key_size, md[i].value_size, - md[i].max_entries, md[i].map_flags)); + struct bpf_create_map_attr attr = { + .name = mapNames[i].c_str(), + .map_type = type, + .map_flags = md[i].map_flags, + .key_size = md[i].key_size, + .value_size = md[i].value_size, + .max_entries = md[i].max_entries, + }; + if (btfFd.has_value() && btfTypeIdMap.find(mapNames[i]) != btfTypeIdMap.end()) { + attr.btf_fd = btfFd->get(); + attr.btf_key_type_id = btfTypeIdMap.at(mapNames[i]).first; + attr.btf_value_type_id = btfTypeIdMap.at(mapNames[i]).second; + } + fd.reset(bcc_create_map_xattr(&attr, true)); saved_errno = errno; ALOGD("bpf_create_map name %s, ret: %d\n", mapNames[i].c_str(), fd.get()); } |