bpf loader improvements for better long term compatibility with mainline am: 9217eee3df
Original change: https://android-review.googlesource.com/c/platform/system/bpf/+/1754794 Change-Id: I8e928d4771f51fb01bdb06f59d093517fbff888a
This commit is contained in:
commit
489a4279d5
3 changed files with 225 additions and 14 deletions
|
@ -28,6 +28,12 @@
|
|||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// This is BpfLoader 0.1, we need to define this prior to including bpf_map_def.h
|
||||
// to get the 0.1 struct definitions
|
||||
#define BPFLOADER_VERSION_MAJOR 0u
|
||||
#define BPFLOADER_VERSION_MINOR 1u
|
||||
#define BPFLOADER_VERSION ((BPFLOADER_VERSION_MAJOR << 16) | BPFLOADER_VERSION_MINOR)
|
||||
|
||||
#include "../progs/include/bpf_map_def.h"
|
||||
#include "bpf/BpfUtils.h"
|
||||
#include "include/libbpf_android.h"
|
||||
|
@ -205,6 +211,29 @@ static int readSectionByName(const char* name, ifstream& elfFile, vector<char>&
|
|||
return -2;
|
||||
}
|
||||
|
||||
static unsigned int readSectionUint(const char* name, ifstream& elfFile, unsigned int defVal) {
|
||||
vector<char> theBytes;
|
||||
int ret = readSectionByName(name, elfFile, theBytes);
|
||||
if (ret) {
|
||||
ALOGD("Couldn't find section %s (defaulting to %u [0x%x]).\n", name, defVal, defVal);
|
||||
return defVal;
|
||||
} else if (theBytes.size() < sizeof(unsigned int)) {
|
||||
ALOGE("Section %s too short (defaulting to %u [0x%x]).\n", name, defVal, defVal);
|
||||
return defVal;
|
||||
} else {
|
||||
// decode first 4 bytes as LE32 uint, there will likely be more bytes due to alignment.
|
||||
unsigned int value = static_cast<unsigned char>(theBytes[3]);
|
||||
value <<= 8;
|
||||
value += static_cast<unsigned char>(theBytes[2]);
|
||||
value <<= 8;
|
||||
value += static_cast<unsigned char>(theBytes[1]);
|
||||
value <<= 8;
|
||||
value += static_cast<unsigned char>(theBytes[0]);
|
||||
ALOGI("Section %s value is %u [0x%x]\n", name, value, value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
static int readSectionByType(ifstream& elfFile, int type, vector<char>& data) {
|
||||
int ret;
|
||||
vector<Elf64_Shdr> shTable;
|
||||
|
@ -281,14 +310,36 @@ static bool isRelSection(codeSection& cs, string& name) {
|
|||
return false;
|
||||
}
|
||||
|
||||
static int readProgDefs(ifstream& elfFile, vector<struct bpf_prog_def>& pd) {
|
||||
static int readProgDefs(ifstream& elfFile, vector<struct bpf_prog_def>& pd,
|
||||
size_t sizeOfBpfProgDef) {
|
||||
vector<char> pdData;
|
||||
int ret = readSectionByName("progs", elfFile, pdData);
|
||||
// Older file formats do not require a 'progs' section at all.
|
||||
// (We should probably figure out whether this is behaviour which is safe to remove now.)
|
||||
if (ret == -2) return 0;
|
||||
if (ret) return ret;
|
||||
|
||||
pd.resize(pdData.size() / sizeof(struct bpf_prog_def));
|
||||
memcpy(pd.data(), pdData.data(), pdData.size());
|
||||
if (pdData.size() % sizeOfBpfProgDef) {
|
||||
ALOGE("readProgDefs failed due to improper sized progs section, %zu %% %zu != 0\n",
|
||||
pdData.size(), sizeOfBpfProgDef);
|
||||
return -1;
|
||||
};
|
||||
|
||||
int progCount = pdData.size() / sizeOfBpfProgDef;
|
||||
pd.resize(progCount);
|
||||
size_t trimmedSize = std::min(sizeOfBpfProgDef, sizeof(struct bpf_prog_def));
|
||||
|
||||
const char* dataPtr = pdData.data();
|
||||
for (auto& p : pd) {
|
||||
// First we zero initialize
|
||||
memset(&p, 0, sizeof(p));
|
||||
// Then we set non-zero defaults
|
||||
p.bpfloader_max_ver = DEFAULT_BPFLOADER_MAX_VER; // v1.0
|
||||
// Then we copy over the structure prefix from the ELF file.
|
||||
memcpy(&p, dataPtr, trimmedSize);
|
||||
// Move to next struct in the ELF file
|
||||
dataPtr += sizeOfBpfProgDef;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -335,7 +386,7 @@ static int getSectionSymNames(ifstream& elfFile, const string& sectionName, vect
|
|||
}
|
||||
|
||||
/* Read a section by its index - for ex to get sec hdr strtab blob */
|
||||
static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs) {
|
||||
static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs, size_t sizeOfBpfProgDef) {
|
||||
vector<Elf64_Shdr> shTable;
|
||||
int entries, ret = 0;
|
||||
|
||||
|
@ -344,7 +395,7 @@ static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs) {
|
|||
entries = shTable.size();
|
||||
|
||||
vector<struct bpf_prog_def> pd;
|
||||
ret = readProgDefs(elfFile, pd);
|
||||
ret = readProgDefs(elfFile, pd, sizeOfBpfProgDef);
|
||||
if (ret) return ret;
|
||||
vector<string> progDefNames;
|
||||
ret = getSectionSymNames(elfFile, "progs", progDefNames);
|
||||
|
@ -416,7 +467,7 @@ static int getSymNameByIdx(ifstream& elfFile, int index, string& name) {
|
|||
}
|
||||
|
||||
static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& mapFds,
|
||||
const char* prefix) {
|
||||
const char* prefix, size_t sizeOfBpfMapDef) {
|
||||
int ret;
|
||||
vector<char> mdData;
|
||||
vector<struct bpf_map_def> md;
|
||||
|
@ -426,8 +477,28 @@ static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>&
|
|||
ret = readSectionByName("maps", elfFile, mdData);
|
||||
if (ret == -2) return 0; // no maps to read
|
||||
if (ret) return ret;
|
||||
md.resize(mdData.size() / sizeof(struct bpf_map_def));
|
||||
memcpy(md.data(), mdData.data(), mdData.size());
|
||||
|
||||
if (mdData.size() % sizeOfBpfMapDef) {
|
||||
ALOGE("createMaps failed due to improper sized maps section, %zu %% %zu != 0\n",
|
||||
mdData.size(), sizeOfBpfMapDef);
|
||||
return -1;
|
||||
};
|
||||
|
||||
int mapCount = mdData.size() / sizeOfBpfMapDef;
|
||||
md.resize(mapCount);
|
||||
size_t trimmedSize = std::min(sizeOfBpfMapDef, sizeof(struct bpf_map_def));
|
||||
|
||||
const char* dataPtr = mdData.data();
|
||||
for (auto& m : md) {
|
||||
// First we zero initialize
|
||||
memset(&m, 0, sizeof(m));
|
||||
// Then we set non-zero defaults
|
||||
m.bpfloader_max_ver = DEFAULT_BPFLOADER_MAX_VER; // v1.0
|
||||
// Then we copy over the structure prefix from the ELF file.
|
||||
memcpy(&m, dataPtr, trimmedSize);
|
||||
// Move to next struct in the ELF file
|
||||
dataPtr += sizeOfBpfMapDef;
|
||||
}
|
||||
|
||||
ret = getSectionSymNames(elfFile, "maps", mapNames);
|
||||
if (ret) return ret;
|
||||
|
@ -436,10 +507,24 @@ static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>&
|
|||
unique_fd fd;
|
||||
int saved_errno;
|
||||
// Format of pin location is /sys/fs/bpf/<prefix>map_<filename>_<mapname>
|
||||
string mapPinLoc;
|
||||
string mapPinLoc =
|
||||
string(BPF_FS_PATH) + prefix + "map_" + fname + "_" + string(mapNames[i]);
|
||||
bool reuse = false;
|
||||
|
||||
mapPinLoc = string(BPF_FS_PATH) + prefix + "map_" + fname + "_" + string(mapNames[i]);
|
||||
if (BPFLOADER_VERSION < md[i].bpfloader_min_ver) {
|
||||
ALOGI("skipping map %s which requires bpfloader min ver 0x%05x\n", mapNames[i].c_str(),
|
||||
md[i].bpfloader_min_ver);
|
||||
mapFds.push_back(std::move(fd)); // -1
|
||||
continue;
|
||||
}
|
||||
|
||||
if (BPFLOADER_VERSION >= md[i].bpfloader_max_ver) {
|
||||
ALOGI("skipping map %s which requires bpfloader max ver 0x%05x\n", mapNames[i].c_str(),
|
||||
md[i].bpfloader_max_ver);
|
||||
mapFds.push_back(std::move(fd)); // -1
|
||||
continue;
|
||||
}
|
||||
|
||||
if (access(mapPinLoc.c_str(), F_OK) == 0) {
|
||||
fd.reset(bpf_obj_get(mapPinLoc.c_str()));
|
||||
saved_errno = errno;
|
||||
|
@ -574,6 +659,8 @@ static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const
|
|||
|
||||
for (int i = 0; i < (int)cs.size(); i++) {
|
||||
string name = cs[i].name;
|
||||
unsigned bpfMinVer = DEFAULT_BPFLOADER_MIN_VER; // v0.0
|
||||
unsigned bpfMaxVer = DEFAULT_BPFLOADER_MAX_VER; // v1.0
|
||||
|
||||
if (cs[i].prog_def.has_value()) {
|
||||
unsigned min_kver = cs[i].prog_def->min_kver;
|
||||
|
@ -582,8 +669,16 @@ static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const
|
|||
max_kver, kvers);
|
||||
if (kvers < min_kver) continue;
|
||||
if (kvers >= max_kver) continue;
|
||||
|
||||
bpfMinVer = cs[i].prog_def->bpfloader_min_ver;
|
||||
bpfMaxVer = cs[i].prog_def->bpfloader_max_ver;
|
||||
}
|
||||
|
||||
ALOGD("cs[%d].name:%s requires bpfloader version [0x%05x,0x%05x)\n", i, name.c_str(),
|
||||
bpfMinVer, bpfMaxVer);
|
||||
if (BPFLOADER_VERSION < bpfMinVer) continue;
|
||||
if (BPFLOADER_VERSION >= bpfMaxVer) continue;
|
||||
|
||||
// strip any potential $foo suffix
|
||||
// this can be used to provide duplicate programs
|
||||
// conditionally loaded based on running kernel version
|
||||
|
@ -674,7 +769,46 @@ int loadProg(const char* elfPath, bool* isCritical, const char* prefix) {
|
|||
elfPath, (char*)license.data());
|
||||
}
|
||||
|
||||
ret = readCodeSections(elfFile, cs);
|
||||
// the following default values are for bpfloader V0.0 format which does not include them
|
||||
unsigned int bpfLoaderMinVer =
|
||||
readSectionUint("bpfloader_min_ver", elfFile, DEFAULT_BPFLOADER_MIN_VER);
|
||||
unsigned int bpfLoaderMaxVer =
|
||||
readSectionUint("bpfloader_max_ver", elfFile, DEFAULT_BPFLOADER_MAX_VER);
|
||||
size_t sizeOfBpfMapDef =
|
||||
readSectionUint("size_of_bpf_map_def", elfFile, DEFAULT_SIZEOF_BPF_MAP_DEF);
|
||||
size_t sizeOfBpfProgDef =
|
||||
readSectionUint("size_of_bpf_prog_def", elfFile, DEFAULT_SIZEOF_BPF_PROG_DEF);
|
||||
|
||||
// inclusive lower bound check
|
||||
if (BPFLOADER_VERSION < bpfLoaderMinVer) {
|
||||
ALOGI("BpfLoader version 0x%05x ignoring ELF object %s with min ver 0x%05x\n",
|
||||
BPFLOADER_VERSION, elfPath, bpfLoaderMinVer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// exclusive upper bound check
|
||||
if (BPFLOADER_VERSION >= bpfLoaderMaxVer) {
|
||||
ALOGI("BpfLoader version 0x%05x ignoring ELF object %s with max ver 0x%05x\n",
|
||||
BPFLOADER_VERSION, elfPath, bpfLoaderMaxVer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ALOGI("BpfLoader version 0x%05x processing ELF object %s with ver [0x%05x,0x%05x)\n",
|
||||
BPFLOADER_VERSION, elfPath, bpfLoaderMinVer, bpfLoaderMaxVer);
|
||||
|
||||
if (sizeOfBpfMapDef < DEFAULT_SIZEOF_BPF_MAP_DEF) {
|
||||
ALOGE("sizeof(bpf_map_def) of %zu is too small (< %d)\n", sizeOfBpfMapDef,
|
||||
DEFAULT_SIZEOF_BPF_MAP_DEF);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sizeOfBpfProgDef < DEFAULT_SIZEOF_BPF_PROG_DEF) {
|
||||
ALOGE("sizeof(bpf_prog_def) of %zu is too small (< %d)\n", sizeOfBpfProgDef,
|
||||
DEFAULT_SIZEOF_BPF_PROG_DEF);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = readCodeSections(elfFile, cs, sizeOfBpfProgDef);
|
||||
if (ret) {
|
||||
ALOGE("Couldn't read all code sections in %s\n", elfPath);
|
||||
return ret;
|
||||
|
@ -683,7 +817,7 @@ int loadProg(const char* elfPath, bool* isCritical, const char* prefix) {
|
|||
/* Just for future debugging */
|
||||
if (0) dumpAllCs(cs);
|
||||
|
||||
ret = createMaps(elfPath, elfFile, mapFds, prefix);
|
||||
ret = createMaps(elfPath, elfFile, mapFds, prefix, sizeOfBpfMapDef);
|
||||
if (ret) {
|
||||
ALOGE("Failed to create maps: (ret=%d) in %s\n", ret, elfPath);
|
||||
return ret;
|
||||
|
|
|
@ -6,11 +6,47 @@
|
|||
|
||||
#include "bpf_map_def.h"
|
||||
|
||||
/******************************************************************************
|
||||
* WARNING: CHANGES TO THIS FILE OUTSIDE OF AOSP/MASTER ARE LIKELY TO BREAK *
|
||||
* DEVICE COMPATIBILITY WITH MAINLINE MODULES SHIPPING EBPF CODE. *
|
||||
* *
|
||||
* THIS WILL LIKELY RESULT IN BRICKED DEVICES AT SOME ARBITRARY FUTURE TIME *
|
||||
* *
|
||||
* THAT GOES ESPECIALLY FOR THE 'SEC' 'LICENSE' AND 'CRITICAL' MACRO DEFINES *
|
||||
* *
|
||||
* We strongly suggest that if you need changes to bpfloader functionality *
|
||||
* you get your changes reviewed and accepted into aosp/master. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
/* place things in different elf sections */
|
||||
#define SEC(NAME) __attribute__((section(NAME), used))
|
||||
|
||||
/* Example use: LICENSE("GPL"); or LICENSE("Apache 2.0"); */
|
||||
#define LICENSE(NAME) char _license[] SEC("license") = (NAME)
|
||||
/* Must be present in every program, example usage:
|
||||
* LICENSE("GPL"); or LICENSE("Apache 2.0");
|
||||
*
|
||||
* We also take this opportunity to embed a bunch of other useful values in
|
||||
* the resulting .o (This is to enable some limited forward compatibility
|
||||
* with mainline module shipped ebpf programs)
|
||||
*
|
||||
* The bpfloader_{min/max}_ver defines the [min, max) range of bpfloader
|
||||
* versions that should load this .o file (bpfloaders outside of this range
|
||||
* will simply ignore/skip this *entire* .o)
|
||||
* The [inclusive,exclusive) matches what we do for kernel ver dependencies.
|
||||
*
|
||||
* The size_of_bpf_{map,prog}_def allow the bpfloader to load programs where
|
||||
* these structures have been extended with additional fields (they will of
|
||||
* course simply be ignored then).
|
||||
*
|
||||
* If missing, bpfloader_{min/max}_ver default to 0/0x10000 ie. [v0.0, v1.0),
|
||||
* while size_of_bpf_{map/prog}_def default to 32/20 which are the v0.0 sizes.
|
||||
*/
|
||||
#define LICENSE(NAME) \
|
||||
unsigned int _bpfloader_min_ver SEC("bpfloader_min_ver") = DEFAULT_BPFLOADER_MIN_VER; \
|
||||
unsigned int _bpfloader_max_ver SEC("bpfloader_max_ver") = DEFAULT_BPFLOADER_MAX_VER; \
|
||||
size_t _size_of_bpf_map_def SEC("size_of_bpf_map_def") = sizeof(struct bpf_map_def); \
|
||||
size_t _size_of_bpf_prog_def SEC("size_of_bpf_prog_def") = sizeof(struct bpf_prog_def); \
|
||||
char _license[] SEC("license") = (NAME)
|
||||
|
||||
/* flag the resulting bpf .o file as critical to system functionality,
|
||||
* loading all kernel version appropriate programs in it must succeed
|
||||
|
@ -68,6 +104,7 @@ static int (*bpf_map_delete_elem_unsafe)(const struct bpf_map_def* map,
|
|||
.key_size = sizeof(TypeOfKey), \
|
||||
.value_size = sizeof(TypeOfValue), \
|
||||
.max_entries = (num_entries), \
|
||||
.map_flags = 0, \
|
||||
.uid = (usr), \
|
||||
.gid = (grp), \
|
||||
.mode = (md), \
|
||||
|
|
|
@ -25,6 +25,34 @@
|
|||
// Pull in AID_* constants from //system/core/libcutils/include/private/android_filesystem_config.h
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
/******************************************************************************
|
||||
* *
|
||||
* ! ! ! W A R N I N G ! ! ! *
|
||||
* *
|
||||
* CHANGES TO THESE STRUCTURE DEFINITIONS OUTSIDE OF AOSP/MASTER *WILL* BREAK *
|
||||
* MAINLINE MODULE COMPATIBILITY *
|
||||
* *
|
||||
* AND THUS MAY RESULT IN YOUR DEVICE BRICKING AT SOME ARBITRARY POINT IN *
|
||||
* THE FUTURE *
|
||||
* *
|
||||
* (and even in aosp/master you may only append new fields at the very end, *
|
||||
* you may *never* delete fields, change their types, ordering, insert in *
|
||||
* the middle, etc. If a mainline module using the old definition has *
|
||||
* already shipped (which happens roughly monthly), then it's set in stone) *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
// For now we default to v0.0 format
|
||||
#ifndef BPFLOADER_VERSION
|
||||
#define BPFLOADER_VERSION 0u
|
||||
#endif
|
||||
|
||||
// These are the values used if these fields are missing
|
||||
#define DEFAULT_BPFLOADER_MIN_VER 0u // v0.0
|
||||
#define DEFAULT_BPFLOADER_MAX_VER 0x10000u // v1.0
|
||||
#define DEFAULT_SIZEOF_BPF_MAP_DEF 32 // v0.0 struct: enum + alignment padding + 7 uint
|
||||
#define DEFAULT_SIZEOF_BPF_PROG_DEF 20 // v0.0 struct: 4 uint + bool + alignment padding
|
||||
|
||||
/*
|
||||
* Map structure to be used by Android eBPF C programs. The Android eBPF loader
|
||||
* uses this structure from eBPF object to create maps at boot time.
|
||||
|
@ -51,6 +79,12 @@ struct bpf_map_def {
|
|||
unsigned int uid; // uid_t
|
||||
unsigned int gid; // gid_t
|
||||
unsigned int mode; // mode_t
|
||||
|
||||
#if BPFLOADER_VERSION >= 1u
|
||||
// The following fields were added in version 0.1
|
||||
unsigned int bpfloader_min_ver; // if missing, defaults to 0, ie. v0.0
|
||||
unsigned int bpfloader_max_ver; // if missing, defaults to 0x10000, ie. v1.0
|
||||
#endif
|
||||
};
|
||||
|
||||
struct bpf_prog_def {
|
||||
|
@ -62,4 +96,10 @@ struct bpf_prog_def {
|
|||
unsigned int max_kver;
|
||||
|
||||
bool optional; // program section (ie. function) may fail to load, continue onto next func.
|
||||
|
||||
#if BPFLOADER_VERSION >= 1u
|
||||
// The following fields were added in version 0.1
|
||||
unsigned int bpfloader_min_ver; // if missing, defaults to 0, ie. v0.0
|
||||
unsigned int bpfloader_max_ver; // if missing, defaults to 0x10000, ie. v1.0
|
||||
#endif
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue