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