Initial checkin for relocatable ELF loader and link with bcc
am: d76a2004a6
Change-Id: I292123efc24bc6a761353cc829a2e8596c1c4547
diff --git a/bpfloader/Android.bp b/bpfloader/Android.bp
index eea547d..9f92ce3 100644
--- a/bpfloader/Android.bp
+++ b/bpfloader/Android.bp
@@ -37,6 +37,7 @@
"liblog",
"libnetdbpf",
"libnetdutils",
+ "libbpf",
],
srcs: [
"BpfLoader.cpp",
@@ -44,6 +45,8 @@
required: [
"bpf_kern.o",
+// Uncomment once security related patches ready
+// "time_in_state.o",
],
}
diff --git a/bpfloader/BpfLoader.cpp b/bpfloader/BpfLoader.cpp
index e173e67..a09af29 100644
--- a/bpfloader/BpfLoader.cpp
+++ b/bpfloader/BpfLoader.cpp
@@ -19,6 +19,7 @@
#endif
#include <arpa/inet.h>
+#include <dirent.h>
#include <elf.h>
#include <error.h>
#include <fcntl.h>
@@ -38,19 +39,22 @@
#include <sys/types.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <libbpf_android.h>
#include <log/log.h>
-
#include <netdutils/Misc.h>
#include <netdutils/Slice.h>
#include "bpf/BpfUtils.h"
#include "netdbpf/bpf_shared.h"
+using android::base::EndsWith;
using android::base::unique_fd;
using android::netdutils::Slice;
+using std::string;
-#define BPF_PROG_PATH "/system/etc/bpf"
-#define BPF_PROG_SRC BPF_PROG_PATH "/bpf_kern.o"
+#define BPF_PROG_PATH "/system/etc/bpf/"
+#define BPF_PROG_SRC BPF_PROG_PATH "bpf_kern.o"
#define CLEANANDEXIT(ret, mapPatterns) \
do { \
@@ -65,7 +69,28 @@
using android::bpf::BpfMapInfo;
using android::bpf::BpfProgInfo;
+void loadAllElfObjects(void) {
+ DIR* dir;
+ struct dirent* ent;
+
+ if ((dir = opendir(BPF_PROG_PATH)) != NULL) {
+ while ((ent = readdir(dir)) != NULL) {
+ string s = ent->d_name;
+ if (!EndsWith(s, ".o")) continue;
+
+ string progPath = BPF_PROG_PATH + s;
+
+ int ret = android::bpf::loadProg(progPath.c_str());
+ ALOGI("Attempted load object: %s, ret: %s", progPath.c_str(), std::strerror(-ret));
+ }
+ closedir(dir);
+ }
+}
+
int main() {
+ // Load all ELF objects, create programs and maps, and pin them
+ loadAllElfObjects();
+
const std::vector<BpfMapInfo> mapPatterns = {
BpfMapInfo(COOKIE_TAG_MAP, COOKIE_TAG_MAP_PATH),
BpfMapInfo(UID_COUNTERSET_MAP, UID_COUNTERSET_MAP_PATH),
diff --git a/libbpf_android/Android.bp b/libbpf_android/Android.bp
index 2ffacf3..721792f 100644
--- a/libbpf_android/Android.bp
+++ b/libbpf_android/Android.bp
@@ -34,6 +34,7 @@
android: {
srcs: [
"BpfUtils.cpp",
+ "Loader.cpp",
],
sanitize: {
misc_undefined: ["integer"],
@@ -46,6 +47,7 @@
"libutils",
"liblog",
"libnetdutils",
+ "libbpf",
],
header_libs: [
"libbpf_android_headers"
diff --git a/libbpf_android/Loader.cpp b/libbpf_android/Loader.cpp
new file mode 100644
index 0000000..332c0b4
--- /dev/null
+++ b/libbpf_android/Loader.cpp
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "LibBpfLoader"
+
+#include <errno.h>
+#include <linux/bpf.h>
+#include <linux/elf.h>
+#include <log/log.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include "LoaderUtils.h"
+#include "include/libbpf_android.h"
+
+#include <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include <android-base/strings.h>
+
+#define BPF_FS_PATH "/sys/fs/bpf/"
+
+// Size of the BPF log buffer for verifier logging
+#define BPF_LOAD_LOG_SZ 0x1ffff
+
+using android::base::StartsWith;
+using std::ifstream;
+using std::ios;
+using std::vector;
+
+namespace android {
+namespace bpf {
+
+typedef struct {
+ const char* name;
+ enum bpf_prog_type type;
+} sectionType;
+
+/*
+ * Map section name prefixes to program types, the section name will be:
+ * SEC(<prefix>/<name-of-program>)
+ * For example:
+ * SEC("tracepoint/sched_switch_func") where sched_switch_funcs
+ * is the name of the program, and tracepoint is the type.
+ */
+sectionType sectionNameTypes[] = {
+ { "kprobe", BPF_PROG_TYPE_KPROBE },
+ { "tracepoint", BPF_PROG_TYPE_TRACEPOINT },
+ { "skfilter", BPF_PROG_TYPE_SOCKET_FILTER },
+ { "cgroupskb", BPF_PROG_TYPE_CGROUP_SKB },
+
+ /* End of table */
+ { "END", BPF_PROG_TYPE_UNSPEC },
+};
+
+typedef struct {
+ enum bpf_prog_type type;
+ string name;
+ vector<char> data;
+ vector<char> rel_data;
+
+ int prog_fd; /* fd after loading */
+} codeSection;
+
+/* Common with the eBPF C program */
+struct bpf_map_def {
+ enum bpf_map_type type;
+ unsigned int key_size;
+ unsigned int value_size;
+ unsigned int max_entries;
+ unsigned int map_flags;
+ unsigned int inner_map_idx;
+ unsigned int numa_node;
+};
+
+static int readElfHeader(ifstream& elfFile, Elf64_Ehdr* eh) {
+ elfFile.seekg(0);
+ if (elfFile.fail()) return -1;
+
+ if (!elfFile.read((char*)eh, sizeof(*eh))) return -1;
+
+ return 0;
+}
+
+/* Reads all section header tables into an Shdr array */
+static int readSectionHeadersAll(ifstream& elfFile, vector<Elf64_Shdr>& shTable) {
+ Elf64_Ehdr eh;
+ int ret = 0;
+
+ ret = readElfHeader(elfFile, &eh);
+ if (ret) return ret;
+
+ elfFile.seekg(eh.e_shoff);
+ if (elfFile.fail()) return -1;
+
+ /* Read shdr table entries */
+ shTable.resize(eh.e_shnum);
+
+ if (!elfFile.read((char*)shTable.data(), (eh.e_shnum * eh.e_shentsize))) return -ENOMEM;
+
+ return 0;
+}
+
+/* Read a section by its index - for ex to get sec hdr strtab blob */
+static int readSectionByIdx(ifstream& elfFile, int id, vector<char>& sec) {
+ vector<Elf64_Shdr> shTable;
+ int entries, ret = 0;
+
+ ret = readSectionHeadersAll(elfFile, shTable);
+ if (ret) return ret;
+ entries = shTable.size();
+
+ elfFile.seekg(shTable[id].sh_offset);
+ if (elfFile.fail()) return -1;
+
+ sec.resize(shTable[id].sh_size);
+ if (!elfFile.read(sec.data(), shTable[id].sh_size)) return -1;
+
+ return 0;
+}
+
+/* Read whole section header string table */
+static int readSectionHeaderStrtab(ifstream& elfFile, vector<char>& strtab) {
+ Elf64_Ehdr eh;
+ int ret = 0;
+
+ ret = readElfHeader(elfFile, &eh);
+ if (ret) return ret;
+
+ ret = readSectionByIdx(elfFile, eh.e_shstrndx, strtab);
+ if (ret) return ret;
+
+ return 0;
+}
+
+/* Get name from offset in strtab */
+static int getSymName(ifstream& elfFile, int nameOff, string& name) {
+ int ret;
+ vector<char> secStrTab;
+
+ ret = readSectionHeaderStrtab(elfFile, secStrTab);
+ if (ret) return ret;
+
+ if (nameOff >= (int)secStrTab.size()) return -1;
+
+ name = string((char*)secStrTab.data() + nameOff);
+ return 0;
+}
+
+/* Reads a full section by name - example to get the GPL license */
+static int readSectionByName(const char* name, ifstream& elfFile, vector<char>& data) {
+ vector<char> secStrTab;
+ vector<Elf64_Shdr> shTable;
+ int ret;
+
+ ret = readSectionHeadersAll(elfFile, shTable);
+ if (ret) return ret;
+
+ ret = readSectionHeaderStrtab(elfFile, secStrTab);
+ if (ret) return ret;
+
+ for (int i = 0; i < (int)shTable.size(); i++) {
+ char* secname = secStrTab.data() + shTable[i].sh_name;
+ if (!secname) continue;
+
+ if (!strcmp(secname, name)) {
+ vector<char> dataTmp;
+ dataTmp.resize(shTable[i].sh_size);
+
+ elfFile.seekg(shTable[i].sh_offset);
+ if (elfFile.fail()) return -1;
+
+ if (!elfFile.read((char*)dataTmp.data(), shTable[i].sh_size)) return -1;
+
+ data = dataTmp;
+ return 0;
+ }
+ }
+ return -2;
+}
+
+static int readSectionByType(ifstream& elfFile, int type, vector<char>& data) {
+ int ret;
+ vector<Elf64_Shdr> shTable;
+
+ ret = readSectionHeadersAll(elfFile, shTable);
+ if (ret) return ret;
+
+ for (int i = 0; i < (int)shTable.size(); i++) {
+ if ((int)shTable[i].sh_type != type) continue;
+
+ vector<char> dataTmp;
+ dataTmp.resize(shTable[i].sh_size);
+
+ elfFile.seekg(shTable[i].sh_offset);
+ if (elfFile.fail()) return -1;
+
+ if (!elfFile.read((char*)dataTmp.data(), shTable[i].sh_size)) return -1;
+
+ data = dataTmp;
+ return 0;
+ }
+ return -2;
+}
+
+static bool symCompare(Elf64_Sym a, Elf64_Sym b) {
+ return (a.st_value < b.st_value);
+}
+
+static int readSymTab(ifstream& elfFile, int sort, vector<Elf64_Sym>& data) {
+ int ret, numElems;
+ Elf64_Sym* buf;
+ vector<char> secData;
+
+ ret = readSectionByType(elfFile, SHT_SYMTAB, secData);
+ if (ret) return ret;
+
+ buf = (Elf64_Sym*)secData.data();
+ numElems = (secData.size() / sizeof(Elf64_Sym));
+ data.assign(buf, buf + numElems);
+
+ if (sort) std::sort(data.begin(), data.end(), symCompare);
+ return 0;
+}
+
+static enum bpf_prog_type getSectionType(string& name) {
+ for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++)
+ if (StartsWith(name, sectionNameTypes[i].name)) return sectionNameTypes[i].type;
+
+ return BPF_PROG_TYPE_UNSPEC;
+}
+
+/* If ever needed
+static string getSectionName(enum bpf_prog_type type)
+{
+ for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++)
+ if (sectionNameTypes[i].type == type)
+ return std::string(sectionNameTypes[i].name);
+
+ return NULL;
+}
+*/
+
+static bool isRelSection(codeSection& cs, string& name) {
+ for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++) {
+ sectionType st = sectionNameTypes[i];
+
+ if (st.type != cs.type) continue;
+
+ if (StartsWith(name, std::string(".rel") + st.name + "/"))
+ return true;
+ else
+ return false;
+ }
+ return false;
+}
+
+/* Read a section by its index - for ex to get sec hdr strtab blob */
+static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs) {
+ vector<Elf64_Shdr> shTable;
+ int entries, ret = 0;
+
+ ret = readSectionHeadersAll(elfFile, shTable);
+ if (ret) return ret;
+ entries = shTable.size();
+
+ for (int i = 0; i < entries; i++) {
+ string name;
+ codeSection cs_temp;
+ cs_temp.type = BPF_PROG_TYPE_UNSPEC;
+
+ ret = getSymName(elfFile, shTable[i].sh_name, name);
+ if (ret) return ret;
+
+ enum bpf_prog_type ptype = getSectionType(name);
+ if (ptype != BPF_PROG_TYPE_UNSPEC) {
+ deslash(name);
+ cs_temp.type = ptype;
+ cs_temp.name = name;
+
+ ret = readSectionByIdx(elfFile, i, cs_temp.data);
+ if (ret) return ret;
+ ALOGD("Loaded code section %d (%s)\n", i, name.c_str());
+ }
+
+ /* Check for rel section */
+ if (cs_temp.data.size() > 0 && i < entries) {
+ ret = getSymName(elfFile, shTable[i + 1].sh_name, name);
+ if (ret) return ret;
+
+ if (isRelSection(cs_temp, name)) {
+ ret = readSectionByIdx(elfFile, i + 1, cs_temp.rel_data);
+ if (ret) return ret;
+ ALOGD("Loaded relo section %d (%s)\n", i, name.c_str());
+ }
+ }
+
+ if (cs_temp.data.size() > 0) {
+ cs.push_back(cs_temp);
+ ALOGD("Adding section %d to cs list\n", i);
+ }
+ }
+ return 0;
+}
+
+static int getSymNameByIdx(ifstream& elfFile, int index, string& name) {
+ vector<Elf64_Sym> symtab;
+ int ret = 0;
+
+ ret = readSymTab(elfFile, 0 /* !sort */, symtab);
+ if (ret) return ret;
+
+ if (index >= (int)symtab.size()) return -1;
+
+ return getSymName(elfFile, symtab[index].st_name, name);
+}
+
+static int getMapNames(ifstream& elfFile, vector<string>& names) {
+ int ret;
+ string mapName;
+ vector<Elf64_Sym> symtab;
+ vector<Elf64_Shdr> shTable;
+
+ ret = readSymTab(elfFile, 1 /* sort */, symtab);
+ if (ret) return ret;
+
+ /* Get index of maps section */
+ ret = readSectionHeadersAll(elfFile, shTable);
+ if (ret) return ret;
+
+ int maps_idx = -1;
+ for (int i = 0; i < (int)shTable.size(); i++) {
+ ret = getSymName(elfFile, shTable[i].sh_name, mapName);
+ if (ret) return ret;
+
+ if (!mapName.compare("maps")) {
+ maps_idx = i;
+ break;
+ }
+ }
+
+ /* No maps found */
+ if (maps_idx == -1) {
+ ALOGE("No maps could be found in elf object\n");
+ return -1;
+ }
+
+ for (int i = 0; i < (int)symtab.size(); i++) {
+ if (symtab[i].st_shndx == maps_idx) {
+ string s;
+ ret = getSymName(elfFile, symtab[i].st_name, s);
+ if (ret) return ret;
+ names.push_back(s);
+ }
+ }
+
+ return 0;
+}
+
+static int createMaps(const char* elfPath, ifstream& elfFile, vector<int>& mapFds) {
+ int ret, fd;
+ vector<char> mdData;
+ vector<struct bpf_map_def> md;
+ vector<string> mapNames;
+ string fname = pathToFilename(string(elfPath), true);
+
+ ret = readSectionByName("maps", elfFile, mdData);
+ if (ret) return ret;
+ md.resize(mdData.size() / sizeof(struct bpf_map_def));
+ memcpy(md.data(), mdData.data(), mdData.size());
+
+ ret = getMapNames(elfFile, mapNames);
+ if (ret) return ret;
+
+ mapFds.resize(mapNames.size());
+
+ for (int i = 0; i < (int)mapNames.size(); i++) {
+ // Format of pin location is /sys/fs/bpf/map_<filename>_<mapname>
+ string mapPinLoc;
+ bool reuse = false;
+
+ mapPinLoc = string(BPF_FS_PATH) + "map_" + fname + "_" + string(mapNames[i]);
+ if (access(mapPinLoc.c_str(), F_OK) == 0) {
+ fd = bpf_obj_get(mapPinLoc.c_str());
+ ALOGD("bpf_create_map reusing map %s, ret: %d\n", mapNames[i].c_str(), fd);
+ reuse = true;
+ } else {
+ fd = bpf_create_map(md[i].type, mapNames[i].c_str(), md[i].key_size, md[i].value_size,
+ md[i].max_entries, md[i].map_flags);
+ ALOGD("bpf_create_map name %s, ret: %d\n", mapNames[i].c_str(), fd);
+ }
+
+ if (fd < 0) return fd;
+ if (fd == 0) return -EINVAL;
+
+ if (!reuse) {
+ ret = bpf_obj_pin(fd, mapPinLoc.c_str());
+ if (ret < 0) return ret;
+ }
+
+ mapFds[i] = fd;
+ }
+
+ return ret;
+}
+
+/* For debugging, dump all instructions */
+static void dumpIns(char* ins, int size) {
+ for (int row = 0; row < size / 8; row++) {
+ ALOGE("%d: ", row);
+ for (int j = 0; j < 8; j++) {
+ ALOGE("%3x ", ins[(row * 8) + j]);
+ }
+ ALOGE("\n");
+ }
+}
+
+/* For debugging, dump all code sections from cs list */
+static void dumpAllCs(vector<codeSection>& cs) {
+ for (int i = 0; i < (int)cs.size(); i++) {
+ ALOGE("Dumping cs %d, name %s\n", int(i), cs[i].name.c_str());
+ dumpIns((char*)cs[i].data.data(), cs[i].data.size());
+ ALOGE("-----------\n");
+ }
+}
+
+static void applyRelo(void* insnsPtr, Elf64_Addr offset, int fd) {
+ int insnIndex;
+ struct bpf_insn *insn, *insns;
+
+ insns = (struct bpf_insn*)(insnsPtr);
+
+ insnIndex = offset / sizeof(struct bpf_insn);
+ insn = &insns[insnIndex];
+
+ ALOGD(
+ "applying relo to instruction at byte offset: %d, \
+ insn offset %d , insn %lx\n",
+ (int)offset, (int)insnIndex, *(unsigned long*)insn);
+
+ if (insn->code != (BPF_LD | BPF_IMM | BPF_DW)) {
+ ALOGE("Dumping all instructions till ins %d\n", insnIndex);
+ ALOGE("invalid relo for insn %d: code 0x%x\n", insnIndex, insn->code);
+ dumpIns((char*)insnsPtr, (insnIndex + 3) * 8);
+ return;
+ }
+
+ insn->imm = fd;
+ insn->src_reg = BPF_PSEUDO_MAP_FD;
+}
+
+static void applyMapRelo(ifstream& elfFile, vector<int> mapFds, vector<codeSection>& cs) {
+ vector<string> mapNames;
+
+ int ret = getMapNames(elfFile, mapNames);
+ if (ret) return;
+
+ for (int k = 0; k != (int)cs.size(); k++) {
+ Elf64_Rel* rel = (Elf64_Rel*)(cs[k].rel_data.data());
+ int n_rel = cs[k].rel_data.size() / sizeof(*rel);
+
+ for (int i = 0; i < n_rel; i++) {
+ int symIndex = ELF64_R_SYM(rel[i].r_info);
+ string symName;
+
+ ret = getSymNameByIdx(elfFile, symIndex, symName);
+ if (ret) return;
+
+ /* Find the map fd and apply relo */
+ for (int j = 0; j < (int)mapNames.size(); j++) {
+ if (!mapNames[j].compare(symName)) {
+ applyRelo(cs[k].data.data(), rel[i].r_offset, mapFds[j]);
+ break;
+ }
+ }
+ }
+ }
+}
+
+static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, string license) {
+ int ret, fd, kvers;
+
+ if ((kvers = getMachineKvers()) < 0) return -1;
+
+ string fname = pathToFilename(string(elfPath), true);
+
+ for (int i = 0; i < (int)cs.size(); i++) {
+ string progPinLoc;
+ bool reuse = false;
+
+ // Format of pin location is
+ // /sys/fs/bpf/prog_<filename>_<mapname>
+ progPinLoc = string(BPF_FS_PATH) + "prog_" + fname + "_" + cs[i].name;
+ if (access(progPinLoc.c_str(), F_OK) == 0) {
+ fd = bpf_obj_get(progPinLoc.c_str());
+ ALOGD("New bpf prog load reusing prog %s, ret: %d\n", cs[i].name.c_str(), fd);
+ reuse = true;
+ } else {
+ vector<char> log_buf(BPF_LOAD_LOG_SZ, 0);
+
+ fd = bpf_prog_load(cs[i].type, cs[i].name.c_str(), (struct bpf_insn*)cs[i].data.data(),
+ cs[i].data.size(), license.c_str(), kvers, 0,
+ log_buf.data(), log_buf.size());
+ ALOGD("New bpf core prog_load for %s (%s) returned: %d\n", elfPath, cs[i].name.c_str(),
+ fd);
+
+ if (fd <= 0)
+ ALOGE("bpf_prog_load: log_buf contents: %s\n", (char *)log_buf.data());
+ }
+
+ if (fd < 0) return fd;
+ if (fd == 0) return -EINVAL;
+
+ if (!reuse) {
+ ret = bpf_obj_pin(fd, progPinLoc.c_str());
+ if (ret < 0) return ret;
+ }
+
+ cs[i].prog_fd = fd;
+ }
+
+ return 0;
+}
+
+int loadProg(const char* elfPath) {
+ vector<char> license;
+ vector<codeSection> cs;
+ vector<int> mapFds;
+ int ret;
+
+ ifstream elfFile(elfPath, ios::in | ios::binary);
+ if (!elfFile.is_open()) return -1;
+
+ ret = readSectionByName("license", elfFile, license);
+ if (ret) {
+ ALOGE("Couldn't find license in %s\n", elfPath);
+ return ret;
+ } else {
+ ALOGD("Loading ELF object %s with license %s\n", elfPath, (char*)license.data());
+ }
+
+ ret = readCodeSections(elfFile, cs);
+ if (ret) {
+ ALOGE("Couldn't read all code sections in %s\n", elfPath);
+ return ret;
+ }
+
+ /* Just for future debugging */
+ if (0) dumpAllCs(cs);
+
+ ret = createMaps(elfPath, elfFile, mapFds);
+ if (ret) {
+ ALOGE("Failed to create maps: (ret=%d) in %s\n", ret, elfPath);
+ return ret;
+ }
+
+ for (int i = 0; i < (int)mapFds.size(); i++)
+ ALOGD("map_fd found at %d is %d in %s\n", i, mapFds[i], elfPath);
+
+ applyMapRelo(elfFile, mapFds, cs);
+
+ ret = loadCodeSections(elfPath, cs, string(license.data()));
+ if (ret) ALOGE("Failed to load programs, loadCodeSections ret=%d\n", ret);
+
+ return ret;
+}
+
+} // namespace bpf
+} // namespace android
diff --git a/libbpf_android/LoaderUtils.h b/libbpf_android/LoaderUtils.h
new file mode 100644
index 0000000..f4912e5
--- /dev/null
+++ b/libbpf_android/LoaderUtils.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <iostream>
+#include <vector>
+
+#include <android-base/strings.h>
+
+using android::base::Split;
+using std::string;
+using std::vector;
+
+static string pathToFilename(string path, bool noext = false) {
+ vector<string> spath = android::base::Split(path, "/");
+ string ret = spath.back();
+
+ if (noext) {
+ size_t lastindex = ret.find_last_of(".");
+ return ret.substr(0, lastindex);
+ }
+ return ret;
+}
+
+static int getMachineKvers(void) {
+ struct utsname un;
+ char* unameOut;
+ int nums[3]; // maj, min, sub
+
+ if (uname(&un)) return -1;
+ unameOut = un.release;
+
+ string s = unameOut;
+ string token;
+ size_t pos = 0;
+ int cur_num = 0;
+
+ while ((pos = s.find(".")) != string::npos && cur_num < 3) {
+ token = s.substr(0, pos);
+ s.erase(0, pos + 1);
+
+ if ((pos = token.find("-")) != string::npos) token = token.substr(0, pos);
+
+ nums[cur_num++] = stoi(token);
+ }
+
+ if ((pos = s.find("-")) != string::npos)
+ token = s.substr(0, pos);
+ else
+ token = s;
+
+ if (token.length() > 0 && cur_num < 3) {
+ nums[cur_num++] = stoi(token);
+ }
+
+ if (cur_num != 3)
+ return -1;
+ else
+ return (65536 * nums[0] + 256 * nums[1] + nums[2]);
+}
+
+static void deslash(string& s) {
+ std::replace(s.begin(), s.end(), '/', '_');
+}
diff --git a/libbpf_android/include/libbpf_android.h b/libbpf_android/include/libbpf_android.h
new file mode 100644
index 0000000..cea707c
--- /dev/null
+++ b/libbpf_android/include/libbpf_android.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * Android BPF library - public API
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBBPF_SYSTEM_H
+#define LIBBPF_SYSTEM_H
+
+#include <libbpf.h>
+#include <linux/bpf.h>
+
+namespace android {
+namespace bpf {
+// BPF loader implementation. Loads an eBPF ELF object
+int loadProg(const char* elfpath);
+} // namespace bpf
+} // namespace android
+
+#endif