summaryrefslogtreecommitdiff
path: root/libbpf_android
diff options
context:
space:
mode:
authorConnor O'Brien <connoro@google.com>2022-01-18 21:41:16 -0800
committerConnor O'Brien <connoro@google.com>2022-01-24 21:24:26 +0000
commit35425e5a884342b6b3ce50d990080288e3b3140b (patch)
tree1addf60d269a6bff12b5c8b79e104adf288dfc61 /libbpf_android
parent00fa9635f8ccf36c8ae57b113e80d6c19c5c19c3 (diff)
downloadbpf-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.cpp15
-rw-r--r--libbpf_android/Loader.cpp106
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());
}