2018-10-16 13:19:58 -07:00
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
|
2024-05-01 05:16:51 -07:00
|
|
|
#define LOG_TAG "BpfLoader"
|
2018-10-16 13:19:58 -07:00
|
|
|
|
|
|
|
|
#include <errno.h>
|
2023-04-17 07:16:33 +00:00
|
|
|
#include <fcntl.h>
|
2018-10-16 13:19:58 -07:00
|
|
|
#include <linux/bpf.h>
|
|
|
|
|
#include <linux/elf.h>
|
|
|
|
|
#include <log/log.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
2022-01-18 21:41:16 -08:00
|
|
|
#include <sysexits.h>
|
2020-01-27 03:11:51 -08:00
|
|
|
#include <sys/stat.h>
|
2018-10-16 13:19:58 -07:00
|
|
|
#include <sys/utsname.h>
|
2022-01-18 21:41:16 -08:00
|
|
|
#include <sys/wait.h>
|
2018-10-16 13:19:58 -07:00
|
|
|
#include <unistd.h>
|
|
|
|
|
|
2022-12-14 04:18:02 -08:00
|
|
|
#include "BpfSyscallWrappers.h"
|
2020-02-19 14:23:59 -08:00
|
|
|
#include "bpf/BpfUtils.h"
|
2021-12-20 18:07:21 +08:00
|
|
|
#include "bpf/bpf_map_def.h"
|
2018-10-16 13:19:58 -07:00
|
|
|
#include "include/libbpf_android.h"
|
|
|
|
|
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include <iostream>
|
2020-02-13 21:45:22 -08:00
|
|
|
#include <optional>
|
2018-10-16 13:19:58 -07:00
|
|
|
#include <string>
|
2022-01-18 21:41:16 -08:00
|
|
|
#include <unordered_map>
|
2019-02-01 15:31:26 -08:00
|
|
|
#include <vector>
|
2018-10-16 13:19:58 -07:00
|
|
|
|
2022-01-18 21:41:16 -08:00
|
|
|
#include <android-base/cmsg.h>
|
|
|
|
|
#include <android-base/file.h>
|
2024-08-06 15:32:00 -07:00
|
|
|
#include <android-base/logging.h>
|
2018-10-16 13:19:58 -07:00
|
|
|
#include <android-base/strings.h>
|
2019-10-24 18:23:49 -07:00
|
|
|
#include <android-base/unique_fd.h>
|
2018-10-16 13:19:58 -07:00
|
|
|
|
|
|
|
|
#define BPF_FS_PATH "/sys/fs/bpf/"
|
|
|
|
|
|
|
|
|
|
// Size of the BPF log buffer for verifier logging
|
2021-10-21 17:03:11 -07:00
|
|
|
#define BPF_LOAD_LOG_SZ 0xfffff
|
2018-10-16 13:19:58 -07:00
|
|
|
|
2022-02-03 09:46:01 -08:00
|
|
|
// Unspecified attach type is 0 which is BPF_CGROUP_INET_INGRESS.
|
|
|
|
|
#define BPF_ATTACH_TYPE_UNSPEC BPF_CGROUP_INET_INGRESS
|
|
|
|
|
|
2024-08-06 15:32:00 -07:00
|
|
|
using android::base::EndsWith;
|
2018-10-16 13:19:58 -07:00
|
|
|
using android::base::StartsWith;
|
2019-10-24 18:23:49 -07:00
|
|
|
using android::base::unique_fd;
|
2018-10-16 13:19:58 -07:00
|
|
|
using std::ifstream;
|
|
|
|
|
using std::ios;
|
2020-02-13 21:45:22 -08:00
|
|
|
using std::optional;
|
2024-08-06 15:32:00 -07:00
|
|
|
using std::strerror;
|
2019-02-01 15:31:26 -08:00
|
|
|
using std::string;
|
2018-10-16 13:19:58 -07:00
|
|
|
using std::vector;
|
|
|
|
|
|
|
|
|
|
namespace android {
|
|
|
|
|
namespace bpf {
|
|
|
|
|
|
2023-06-16 08:18:49 +00:00
|
|
|
static unsigned int page_size = static_cast<unsigned int>(getpagesize());
|
|
|
|
|
|
BpfLoader v0.25 - add support for obj@ver.o
This allows for 2 different versions of obj.o to be
shipped simultaneously (via mainline module),
with different bpfloader version limitations.
For example a obj.o for bpfloader < 0.25 and a
obj@25.o for bpfloader 0.25+. These can provide
for different implementations of maps/programs,
while still being pinned into the same ultimate
destination in /sys/fs/bpf/.../{map,prog}_obj_...
so as to not require special selection of appropriate
program/map path names in higher level code
(at least for common functionality).
When using this functionality one does have to be
careful to not end up with unintentional duplication
(ie. an obj@1.o and obj@2.o that both load on bpfloader
version X), and to make sure that the defined
bpf maps and programs with identical names
are also sufficiently identical in behaviour.
In practice it is likely that all versions of obj@ver.o
will be built from the same source code, with compilation
controlled by appropriate preprocessor conditional macros,
to hide certain parts of obj.c while building the version
for older bpfloader...
However, exactly how to use this is ultimately
left up to the future...
Multiple viable mechanisms exist:
(a) each obj@ver.o is standalone, only one should be loaded,
bpfloader min/max version annotations would be used
to guarantee this, making sure that programs/maps that
exist in multiple versions of obj.o should have matching
types and behaviours, but nothing guarantees this
(although key/value size checks will certainly help)
(b) obj.o is baseline and always loaded,
while obj@25.o is an extension with extra maps/programs
and is loaded on only newer bpfloaders,
it may have duplicate defines of shared maps
(likely via #include of some shared header file)
if some of the extra programs also need some of
the data from maps from the 'older' obj.o
(c) variously complex combinations of (a) and (b) are also possible
Bug: 218408035
Test: TreeHugger, manually with offload@1.o in p/m/C
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: Ib60d07b18fd6617d456c2c469f8e8ed166aadffd
2022-07-07 06:01:57 -07:00
|
|
|
static string pathToObjName(const string& path) {
|
|
|
|
|
// extract everything after the final slash, ie. this is the filename 'foo@1.o' or 'bar.o'
|
|
|
|
|
string filename = android::base::Split(path, "/").back();
|
|
|
|
|
// strip off everything from the final period onwards (strip '.o' suffix), ie. 'foo@1' or 'bar'
|
|
|
|
|
string name = filename.substr(0, filename.find_last_of('.'));
|
|
|
|
|
// strip any potential @1 suffix, this will leave us with just 'foo' or 'bar'
|
|
|
|
|
// this can be used to provide duplicate programs (mux based on the bpfloader version)
|
|
|
|
|
return name.substr(0, name.find_last_of('@'));
|
2021-01-21 16:01:04 -08:00
|
|
|
}
|
|
|
|
|
|
2018-10-16 13:19:58 -07:00
|
|
|
typedef struct {
|
|
|
|
|
const char* name;
|
|
|
|
|
enum bpf_prog_type type;
|
2022-02-03 09:46:01 -08:00
|
|
|
enum bpf_attach_type expected_attach_type;
|
2018-10-16 13:19:58 -07:00
|
|
|
} sectionType;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Map section name prefixes to program types, the section name will be:
|
2021-10-22 19:27:10 -07:00
|
|
|
* SECTION(<prefix>/<name-of-program>)
|
2018-10-16 13:19:58 -07:00
|
|
|
* For example:
|
2021-10-22 19:27:10 -07:00
|
|
|
* SECTION("tracepoint/sched_switch_func") where sched_switch_funcs
|
2018-10-16 13:19:58 -07:00
|
|
|
* is the name of the program, and tracepoint is the type.
|
2021-10-22 19:27:10 -07:00
|
|
|
*
|
|
|
|
|
* However, be aware that you should not be directly using the SECTION() macro.
|
|
|
|
|
* Instead use the DEFINE_(BPF|XDP)_(PROG|MAP)... & LICENSE/CRITICAL macros.
|
2018-10-16 13:19:58 -07:00
|
|
|
*/
|
|
|
|
|
sectionType sectionNameTypes[] = {
|
2023-04-05 01:16:05 +00:00
|
|
|
{"kprobe/", BPF_PROG_TYPE_KPROBE, BPF_ATTACH_TYPE_UNSPEC},
|
|
|
|
|
{"kretprobe/", BPF_PROG_TYPE_KPROBE, BPF_ATTACH_TYPE_UNSPEC},
|
|
|
|
|
{"perf_event/", BPF_PROG_TYPE_PERF_EVENT, BPF_ATTACH_TYPE_UNSPEC},
|
|
|
|
|
{"skfilter/", BPF_PROG_TYPE_SOCKET_FILTER, BPF_ATTACH_TYPE_UNSPEC},
|
|
|
|
|
{"tracepoint/", BPF_PROG_TYPE_TRACEPOINT, BPF_ATTACH_TYPE_UNSPEC},
|
|
|
|
|
{"uprobe/", BPF_PROG_TYPE_KPROBE, BPF_ATTACH_TYPE_UNSPEC},
|
|
|
|
|
{"uretprobe/", BPF_PROG_TYPE_KPROBE, BPF_ATTACH_TYPE_UNSPEC},
|
2018-10-16 13:19:58 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
enum bpf_prog_type type;
|
2022-02-03 09:46:01 -08:00
|
|
|
enum bpf_attach_type expected_attach_type;
|
2018-10-16 13:19:58 -07:00
|
|
|
string name;
|
|
|
|
|
vector<char> data;
|
|
|
|
|
vector<char> rel_data;
|
2020-02-13 21:45:22 -08:00
|
|
|
optional<struct bpf_prog_def> prog_def;
|
2018-10-16 13:19:58 -07:00
|
|
|
|
2019-10-24 18:23:49 -07:00
|
|
|
unique_fd prog_fd; /* fd after loading */
|
2018-10-16 13:19:58 -07:00
|
|
|
} codeSection;
|
|
|
|
|
|
|
|
|
|
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;
|
2021-01-15 00:27:04 -08:00
|
|
|
int ret = readSectionHeadersAll(elfFile, shTable);
|
2018-10-16 13:19:58 -07:00
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
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;
|
2021-01-15 00:27:04 -08:00
|
|
|
int ret = readElfHeader(elfFile, &eh);
|
2018-10-16 13:19:58 -07:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-06 01:47:15 -07:00
|
|
|
unsigned int readSectionUint(const char* name, ifstream& elfFile, unsigned int defVal) {
|
2021-03-03 05:28:52 -08:00
|
|
|
vector<char> theBytes;
|
|
|
|
|
int ret = readSectionByName(name, elfFile, theBytes);
|
|
|
|
|
if (ret) {
|
2024-04-30 22:35:08 -07:00
|
|
|
ALOGV("Couldn't find section %s (defaulting to %u [0x%x]).", name, defVal, defVal);
|
2021-03-03 05:28:52 -08:00
|
|
|
return defVal;
|
|
|
|
|
} else if (theBytes.size() < sizeof(unsigned int)) {
|
2022-06-17 02:35:36 -07:00
|
|
|
ALOGE("Section %s too short (defaulting to %u [0x%x]).", name, defVal, defVal);
|
2021-03-03 05:28:52 -08:00
|
|
|
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]);
|
2024-04-30 22:35:08 -07:00
|
|
|
ALOGV("Section %s value is %u [0x%x]", name, value, value);
|
2021-03-03 05:28:52 -08:00
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-16 13:19:58 -07:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-19 16:13:49 -07:00
|
|
|
static enum bpf_prog_type getFuseProgType() {
|
|
|
|
|
int result = BPF_PROG_TYPE_UNSPEC;
|
|
|
|
|
ifstream("/sys/fs/fuse/bpf_prog_type_fuse") >> result;
|
|
|
|
|
return static_cast<bpf_prog_type>(result);
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-16 13:19:58 -07:00
|
|
|
static enum bpf_prog_type getSectionType(string& name) {
|
2021-11-18 15:13:36 -08:00
|
|
|
for (auto& snt : sectionNameTypes)
|
|
|
|
|
if (StartsWith(name, snt.name)) return snt.type;
|
2018-10-16 13:19:58 -07:00
|
|
|
|
2021-11-09 16:32:43 +00:00
|
|
|
// TODO Remove this code when fuse-bpf is upstream and this BPF_PROG_TYPE_FUSE is fixed
|
2022-07-19 16:13:49 -07:00
|
|
|
if (StartsWith(name, "fuse/")) return getFuseProgType();
|
2021-11-09 16:32:43 +00:00
|
|
|
|
2018-10-16 13:19:58 -07:00
|
|
|
return BPF_PROG_TYPE_UNSPEC;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-03 09:46:01 -08:00
|
|
|
static enum bpf_attach_type getExpectedAttachType(string& name) {
|
|
|
|
|
for (auto& snt : sectionNameTypes)
|
|
|
|
|
if (StartsWith(name, snt.name)) return snt.expected_attach_type;
|
|
|
|
|
return BPF_ATTACH_TYPE_UNSPEC;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-16 13:19:58 -07:00
|
|
|
static string getSectionName(enum bpf_prog_type type)
|
|
|
|
|
{
|
2021-11-18 15:13:36 -08:00
|
|
|
for (auto& snt : sectionNameTypes)
|
|
|
|
|
if (snt.type == type)
|
|
|
|
|
return string(snt.name);
|
2018-10-16 13:19:58 -07:00
|
|
|
|
2019-12-12 14:22:34 -08:00
|
|
|
return "UNKNOWN SECTION NAME " + std::to_string(type);
|
2018-10-16 13:19:58 -07:00
|
|
|
}
|
|
|
|
|
|
2024-03-10 14:37:24 -07:00
|
|
|
static int readProgDefs(ifstream& elfFile, vector<struct bpf_prog_def>& pd) {
|
2020-02-13 21:45:22 -08:00
|
|
|
vector<char> pdData;
|
|
|
|
|
int ret = readSectionByName("progs", elfFile, pdData);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
2024-03-10 14:37:24 -07:00
|
|
|
if (pdData.size() % sizeof(struct bpf_prog_def)) {
|
2022-06-17 02:35:36 -07:00
|
|
|
ALOGE("readProgDefs failed due to improper sized progs section, %zu %% %zu != 0",
|
2024-03-10 14:37:24 -07:00
|
|
|
pdData.size(), sizeof(struct bpf_prog_def));
|
2021-03-03 05:28:52 -08:00
|
|
|
return -1;
|
|
|
|
|
};
|
|
|
|
|
|
2024-03-10 14:37:24 -07:00
|
|
|
pd.resize(pdData.size() / sizeof(struct bpf_prog_def));
|
|
|
|
|
memcpy(pd.data(), pdData.data(), pdData.size());
|
2020-02-13 21:45:22 -08:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-14 00:18:11 -08:00
|
|
|
static int getSectionSymNames(ifstream& elfFile, const string& sectionName, vector<string>& names,
|
|
|
|
|
optional<unsigned> symbolType = std::nullopt) {
|
2020-02-13 21:45:22 -08:00
|
|
|
int ret;
|
|
|
|
|
string name;
|
|
|
|
|
vector<Elf64_Sym> symtab;
|
|
|
|
|
vector<Elf64_Shdr> shTable;
|
|
|
|
|
|
|
|
|
|
ret = readSymTab(elfFile, 1 /* sort */, symtab);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
/* Get index of section */
|
|
|
|
|
ret = readSectionHeadersAll(elfFile, shTable);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
int sec_idx = -1;
|
|
|
|
|
for (int i = 0; i < (int)shTable.size(); i++) {
|
|
|
|
|
ret = getSymName(elfFile, shTable[i].sh_name, name);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
if (!name.compare(sectionName)) {
|
|
|
|
|
sec_idx = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No section found with matching name*/
|
|
|
|
|
if (sec_idx == -1) {
|
2022-06-17 02:35:36 -07:00
|
|
|
ALOGW("No %s section could be found in elf object", sectionName.c_str());
|
2020-02-13 21:45:22 -08:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < (int)symtab.size(); i++) {
|
2022-01-14 00:18:11 -08:00
|
|
|
if (symbolType.has_value() && ELF_ST_TYPE(symtab[i].st_info) != symbolType) continue;
|
|
|
|
|
|
2020-02-13 21:45:22 -08:00
|
|
|
if (symtab[i].st_shndx == sec_idx) {
|
|
|
|
|
string s;
|
|
|
|
|
ret = getSymName(elfFile, symtab[i].st_name, s);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
names.push_back(s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-12 14:22:34 -08:00
|
|
|
static bool IsAllowed(bpf_prog_type type, const bpf_prog_type* allowed, size_t numAllowed) {
|
|
|
|
|
if (allowed == nullptr) return true;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < numAllowed; i++) {
|
2022-07-19 16:13:49 -07:00
|
|
|
if (allowed[i] == BPF_PROG_TYPE_UNSPEC) {
|
|
|
|
|
if (type == getFuseProgType()) return true;
|
|
|
|
|
} else if (type == allowed[i])
|
|
|
|
|
return true;
|
2019-12-12 14:22:34 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-16 13:19:58 -07:00
|
|
|
/* Read a section by its index - for ex to get sec hdr strtab blob */
|
2024-03-10 14:37:24 -07:00
|
|
|
static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs,
|
2019-12-12 14:22:34 -08:00
|
|
|
const bpf_prog_type* allowed, size_t numAllowed) {
|
2018-10-16 13:19:58 -07:00
|
|
|
vector<Elf64_Shdr> shTable;
|
|
|
|
|
int entries, ret = 0;
|
|
|
|
|
|
|
|
|
|
ret = readSectionHeadersAll(elfFile, shTable);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
entries = shTable.size();
|
|
|
|
|
|
2020-02-13 21:45:22 -08:00
|
|
|
vector<struct bpf_prog_def> pd;
|
2024-03-10 14:37:24 -07:00
|
|
|
ret = readProgDefs(elfFile, pd);
|
2020-02-13 21:45:22 -08:00
|
|
|
if (ret) return ret;
|
|
|
|
|
vector<string> progDefNames;
|
|
|
|
|
ret = getSectionSymNames(elfFile, "progs", progDefNames);
|
|
|
|
|
if (!pd.empty() && ret) return ret;
|
|
|
|
|
|
2018-10-16 13:19:58 -07:00
|
|
|
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);
|
2019-12-12 14:22:34 -08:00
|
|
|
|
2022-02-03 09:46:01 -08:00
|
|
|
if (ptype == BPF_PROG_TYPE_UNSPEC) continue;
|
2021-01-21 16:01:04 -08:00
|
|
|
|
2019-12-12 14:22:34 -08:00
|
|
|
if (!IsAllowed(ptype, allowed, numAllowed)) {
|
|
|
|
|
ALOGE("Program type %s not permitted here", getSectionName(ptype).c_str());
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-03 09:46:01 -08:00
|
|
|
// This must be done before '/' is replaced with '_'.
|
|
|
|
|
cs_temp.expected_attach_type = getExpectedAttachType(name);
|
2021-01-21 16:01:04 -08:00
|
|
|
|
2022-02-03 09:46:01 -08:00
|
|
|
string oldName = name;
|
2018-10-16 13:19:58 -07:00
|
|
|
|
2022-02-03 09:46:01 -08:00
|
|
|
// convert all slashes to underscores
|
|
|
|
|
std::replace(name.begin(), name.end(), '/', '_');
|
2020-02-13 21:45:22 -08:00
|
|
|
|
2022-02-03 09:46:01 -08:00
|
|
|
cs_temp.type = ptype;
|
|
|
|
|
cs_temp.name = name;
|
|
|
|
|
|
|
|
|
|
ret = readSectionByIdx(elfFile, i, cs_temp.data);
|
|
|
|
|
if (ret) return ret;
|
2024-04-30 22:35:08 -07:00
|
|
|
ALOGV("Loaded code section %d (%s)", i, name.c_str());
|
2022-02-03 09:46:01 -08:00
|
|
|
|
|
|
|
|
vector<string> csSymNames;
|
|
|
|
|
ret = getSectionSymNames(elfFile, oldName, csSymNames, STT_FUNC);
|
|
|
|
|
if (ret || !csSymNames.size()) return ret;
|
|
|
|
|
for (size_t i = 0; i < progDefNames.size(); ++i) {
|
|
|
|
|
if (!progDefNames[i].compare(csSymNames[0] + "_def")) {
|
|
|
|
|
cs_temp.prog_def = pd[i];
|
|
|
|
|
break;
|
2020-02-13 21:45:22 -08:00
|
|
|
}
|
2018-10-16 13:19:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
2022-02-03 09:46:01 -08:00
|
|
|
if (name == (".rel" + oldName)) {
|
2018-10-16 13:19:58 -07:00
|
|
|
ret = readSectionByIdx(elfFile, i + 1, cs_temp.rel_data);
|
|
|
|
|
if (ret) return ret;
|
2024-04-30 22:35:08 -07:00
|
|
|
ALOGV("Loaded relo section %d (%s)", i, name.c_str());
|
2018-10-16 13:19:58 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cs_temp.data.size() > 0) {
|
2019-10-24 18:23:49 -07:00
|
|
|
cs.push_back(std::move(cs_temp));
|
2024-04-30 22:35:08 -07:00
|
|
|
ALOGV("Adding section %d to cs list", i);
|
2018-10-16 13:19:58 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-20 18:16:24 -07:00
|
|
|
static bool mapMatchesExpectations(const unique_fd& fd, const string& mapName,
|
|
|
|
|
const struct bpf_map_def& mapDef, const enum bpf_map_type type) {
|
|
|
|
|
// Assuming fd is a valid Bpf Map file descriptor then
|
|
|
|
|
// all the following should always succeed on a 4.14+ kernel.
|
|
|
|
|
// If they somehow do fail, they'll return -1 (and set errno),
|
|
|
|
|
// which should then cause (among others) a key_size mismatch.
|
2022-06-16 15:50:58 -07:00
|
|
|
int fd_type = bpfGetFdMapType(fd);
|
|
|
|
|
int fd_key_size = bpfGetFdKeySize(fd);
|
|
|
|
|
int fd_value_size = bpfGetFdValueSize(fd);
|
|
|
|
|
int fd_max_entries = bpfGetFdMaxEntries(fd);
|
|
|
|
|
int fd_map_flags = bpfGetFdMapFlags(fd);
|
|
|
|
|
|
|
|
|
|
// DEVMAPs are readonly from the bpf program side's point of view, as such
|
|
|
|
|
// the kernel in kernel/bpf/devmap.c dev_map_init_map() will set the flag
|
|
|
|
|
int desired_map_flags = (int)mapDef.map_flags;
|
|
|
|
|
if (type == BPF_MAP_TYPE_DEVMAP || type == BPF_MAP_TYPE_DEVMAP_HASH)
|
|
|
|
|
desired_map_flags |= BPF_F_RDONLY_PROG;
|
|
|
|
|
|
2023-06-20 19:40:22 +00:00
|
|
|
// The .h file enforces that this is a power of two, and page size will
|
|
|
|
|
// also always be a power of two, so this logic is actually enough to
|
|
|
|
|
// force it to be a multiple of the page size, as required by the kernel.
|
2023-06-16 08:18:49 +00:00
|
|
|
unsigned int desired_max_entries = mapDef.max_entries;
|
|
|
|
|
if (type == BPF_MAP_TYPE_RINGBUF) {
|
|
|
|
|
if (desired_max_entries < page_size) desired_max_entries = page_size;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-20 18:16:24 -07:00
|
|
|
// The following checks should *never* trigger, if one of them somehow does,
|
|
|
|
|
// it probably means a bpf .o file has been changed/replaced at runtime
|
|
|
|
|
// and bpfloader was manually rerun (normally it should only run *once*
|
|
|
|
|
// early during the boot process).
|
|
|
|
|
// Another possibility is that something is misconfigured in the code:
|
|
|
|
|
// most likely a shared map is declared twice differently.
|
|
|
|
|
// But such a change should never be checked into the source tree...
|
|
|
|
|
if ((fd_type == type) &&
|
|
|
|
|
(fd_key_size == (int)mapDef.key_size) &&
|
|
|
|
|
(fd_value_size == (int)mapDef.value_size) &&
|
2023-06-16 08:18:49 +00:00
|
|
|
(fd_max_entries == (int)desired_max_entries) &&
|
2022-06-20 18:16:24 -07:00
|
|
|
(fd_map_flags == desired_map_flags)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-06-16 15:50:58 -07:00
|
|
|
|
|
|
|
|
ALOGE("bpf map name %s mismatch: desired/found: "
|
|
|
|
|
"type:%d/%d key:%u/%d value:%u/%d entries:%u/%d flags:%u/%d",
|
|
|
|
|
mapName.c_str(), type, fd_type, mapDef.key_size, fd_key_size, mapDef.value_size,
|
|
|
|
|
fd_value_size, mapDef.max_entries, fd_max_entries, desired_map_flags, fd_map_flags);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-14 23:36:32 -08:00
|
|
|
static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& mapFds,
|
2024-03-10 19:11:58 -07:00
|
|
|
const char* prefix) {
|
2019-10-24 18:23:49 -07:00
|
|
|
int ret;
|
2023-06-12 23:10:52 -07:00
|
|
|
vector<char> mdData;
|
2018-10-16 13:19:58 -07:00
|
|
|
vector<struct bpf_map_def> md;
|
|
|
|
|
vector<string> mapNames;
|
BpfLoader v0.25 - add support for obj@ver.o
This allows for 2 different versions of obj.o to be
shipped simultaneously (via mainline module),
with different bpfloader version limitations.
For example a obj.o for bpfloader < 0.25 and a
obj@25.o for bpfloader 0.25+. These can provide
for different implementations of maps/programs,
while still being pinned into the same ultimate
destination in /sys/fs/bpf/.../{map,prog}_obj_...
so as to not require special selection of appropriate
program/map path names in higher level code
(at least for common functionality).
When using this functionality one does have to be
careful to not end up with unintentional duplication
(ie. an obj@1.o and obj@2.o that both load on bpfloader
version X), and to make sure that the defined
bpf maps and programs with identical names
are also sufficiently identical in behaviour.
In practice it is likely that all versions of obj@ver.o
will be built from the same source code, with compilation
controlled by appropriate preprocessor conditional macros,
to hide certain parts of obj.c while building the version
for older bpfloader...
However, exactly how to use this is ultimately
left up to the future...
Multiple viable mechanisms exist:
(a) each obj@ver.o is standalone, only one should be loaded,
bpfloader min/max version annotations would be used
to guarantee this, making sure that programs/maps that
exist in multiple versions of obj.o should have matching
types and behaviours, but nothing guarantees this
(although key/value size checks will certainly help)
(b) obj.o is baseline and always loaded,
while obj@25.o is an extension with extra maps/programs
and is loaded on only newer bpfloaders,
it may have duplicate defines of shared maps
(likely via #include of some shared header file)
if some of the extra programs also need some of
the data from maps from the 'older' obj.o
(c) variously complex combinations of (a) and (b) are also possible
Bug: 218408035
Test: TreeHugger, manually with offload@1.o in p/m/C
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: Ib60d07b18fd6617d456c2c469f8e8ed166aadffd
2022-07-07 06:01:57 -07:00
|
|
|
string objName = pathToObjName(string(elfPath));
|
2018-10-16 13:19:58 -07:00
|
|
|
|
|
|
|
|
ret = readSectionByName("maps", elfFile, mdData);
|
2019-12-12 14:21:20 -08:00
|
|
|
if (ret == -2) return 0; // no maps to read
|
2018-10-16 13:19:58 -07:00
|
|
|
if (ret) return ret;
|
2021-03-03 05:28:52 -08:00
|
|
|
|
2024-03-10 14:37:24 -07:00
|
|
|
if (mdData.size() % sizeof(struct bpf_map_def)) {
|
2022-06-17 02:35:36 -07:00
|
|
|
ALOGE("createMaps failed due to improper sized maps section, %zu %% %zu != 0",
|
2024-03-10 14:37:24 -07:00
|
|
|
mdData.size(), sizeof(struct bpf_map_def));
|
2021-03-03 05:28:52 -08:00
|
|
|
return -1;
|
|
|
|
|
}
|
2024-03-10 14:37:24 -07:00
|
|
|
md.resize(mdData.size() / sizeof(struct bpf_map_def));
|
|
|
|
|
memcpy(md.data(), mdData.data(), mdData.size());
|
2018-10-16 13:19:58 -07:00
|
|
|
|
2020-02-13 21:45:22 -08:00
|
|
|
ret = getSectionSymNames(elfFile, "maps", mapNames);
|
2018-10-16 13:19:58 -07:00
|
|
|
if (ret) return ret;
|
|
|
|
|
|
2022-06-22 22:51:07 -07:00
|
|
|
unsigned kvers = kernelVersion();
|
|
|
|
|
|
2021-07-05 15:20:34 -07:00
|
|
|
for (int i = 0; i < (int)mapNames.size(); i++) {
|
2022-07-21 13:27:24 +00:00
|
|
|
if (md[i].zero != 0) abort();
|
|
|
|
|
|
2021-07-05 15:20:34 -07:00
|
|
|
if (kvers < md[i].min_kver) {
|
2024-04-30 22:35:08 -07:00
|
|
|
ALOGD("skipping map %s which requires kernel version 0x%x >= 0x%x",
|
2021-07-05 15:20:34 -07:00
|
|
|
mapNames[i].c_str(), kvers, md[i].min_kver);
|
|
|
|
|
mapFds.push_back(unique_fd());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (kvers >= md[i].max_kver) {
|
2024-04-30 22:35:08 -07:00
|
|
|
ALOGD("skipping map %s which requires kernel version 0x%x < 0x%x",
|
2021-07-05 15:20:34 -07:00
|
|
|
mapNames[i].c_str(), kvers, md[i].max_kver);
|
|
|
|
|
mapFds.push_back(unique_fd());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-16 15:50:58 -07:00
|
|
|
enum bpf_map_type type = md[i].type;
|
|
|
|
|
if (type == BPF_MAP_TYPE_DEVMAP_HASH && !isAtLeastKernelVersion(5, 4, 0)) {
|
|
|
|
|
// On Linux Kernels older than 5.4 this map type doesn't exist, but it can kind
|
|
|
|
|
// of be approximated: HASH has the same userspace visible api.
|
|
|
|
|
// However it cannot be used by ebpf programs in the same way.
|
|
|
|
|
// Since bpf_redirect_map() only requires 4.14, a program using a DEVMAP_HASH map
|
|
|
|
|
// would fail to load (due to trying to redirect to a HASH instead of DEVMAP_HASH).
|
|
|
|
|
// One must thus tag any BPF_MAP_TYPE_DEVMAP_HASH + bpf_redirect_map() using
|
|
|
|
|
// programs as being 5.4+...
|
|
|
|
|
type = BPF_MAP_TYPE_HASH;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-20 19:40:22 +00:00
|
|
|
// The .h file enforces that this is a power of two, and page size will
|
|
|
|
|
// also always be a power of two, so this logic is actually enough to
|
|
|
|
|
// force it to be a multiple of the page size, as required by the kernel.
|
2023-06-16 08:18:49 +00:00
|
|
|
unsigned int max_entries = md[i].max_entries;
|
|
|
|
|
if (type == BPF_MAP_TYPE_RINGBUF) {
|
|
|
|
|
if (max_entries < page_size) max_entries = page_size;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-10 19:11:58 -07:00
|
|
|
// Format of pin location is /sys/fs/bpf/<prefix>map_<objName>_<mapName>
|
BpfLoader v0.25 - add support for obj@ver.o
This allows for 2 different versions of obj.o to be
shipped simultaneously (via mainline module),
with different bpfloader version limitations.
For example a obj.o for bpfloader < 0.25 and a
obj@25.o for bpfloader 0.25+. These can provide
for different implementations of maps/programs,
while still being pinned into the same ultimate
destination in /sys/fs/bpf/.../{map,prog}_obj_...
so as to not require special selection of appropriate
program/map path names in higher level code
(at least for common functionality).
When using this functionality one does have to be
careful to not end up with unintentional duplication
(ie. an obj@1.o and obj@2.o that both load on bpfloader
version X), and to make sure that the defined
bpf maps and programs with identical names
are also sufficiently identical in behaviour.
In practice it is likely that all versions of obj@ver.o
will be built from the same source code, with compilation
controlled by appropriate preprocessor conditional macros,
to hide certain parts of obj.c while building the version
for older bpfloader...
However, exactly how to use this is ultimately
left up to the future...
Multiple viable mechanisms exist:
(a) each obj@ver.o is standalone, only one should be loaded,
bpfloader min/max version annotations would be used
to guarantee this, making sure that programs/maps that
exist in multiple versions of obj.o should have matching
types and behaviours, but nothing guarantees this
(although key/value size checks will certainly help)
(b) obj.o is baseline and always loaded,
while obj@25.o is an extension with extra maps/programs
and is loaded on only newer bpfloaders,
it may have duplicate defines of shared maps
(likely via #include of some shared header file)
if some of the extra programs also need some of
the data from maps from the 'older' obj.o
(c) variously complex combinations of (a) and (b) are also possible
Bug: 218408035
Test: TreeHugger, manually with offload@1.o in p/m/C
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: Ib60d07b18fd6617d456c2c469f8e8ed166aadffd
2022-07-07 06:01:57 -07:00
|
|
|
// except that maps shared across .o's have empty <objName>
|
|
|
|
|
// Note: <objName> refers to the extension-less basename of the .o file (without @ suffix).
|
2024-03-10 19:11:58 -07:00
|
|
|
string mapPinLoc = string(BPF_FS_PATH) + prefix + "map_" +
|
BpfLoader v0.25 - add support for obj@ver.o
This allows for 2 different versions of obj.o to be
shipped simultaneously (via mainline module),
with different bpfloader version limitations.
For example a obj.o for bpfloader < 0.25 and a
obj@25.o for bpfloader 0.25+. These can provide
for different implementations of maps/programs,
while still being pinned into the same ultimate
destination in /sys/fs/bpf/.../{map,prog}_obj_...
so as to not require special selection of appropriate
program/map path names in higher level code
(at least for common functionality).
When using this functionality one does have to be
careful to not end up with unintentional duplication
(ie. an obj@1.o and obj@2.o that both load on bpfloader
version X), and to make sure that the defined
bpf maps and programs with identical names
are also sufficiently identical in behaviour.
In practice it is likely that all versions of obj@ver.o
will be built from the same source code, with compilation
controlled by appropriate preprocessor conditional macros,
to hide certain parts of obj.c while building the version
for older bpfloader...
However, exactly how to use this is ultimately
left up to the future...
Multiple viable mechanisms exist:
(a) each obj@ver.o is standalone, only one should be loaded,
bpfloader min/max version annotations would be used
to guarantee this, making sure that programs/maps that
exist in multiple versions of obj.o should have matching
types and behaviours, but nothing guarantees this
(although key/value size checks will certainly help)
(b) obj.o is baseline and always loaded,
while obj@25.o is an extension with extra maps/programs
and is loaded on only newer bpfloaders,
it may have duplicate defines of shared maps
(likely via #include of some shared header file)
if some of the extra programs also need some of
the data from maps from the 'older' obj.o
(c) variously complex combinations of (a) and (b) are also possible
Bug: 218408035
Test: TreeHugger, manually with offload@1.o in p/m/C
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: Ib60d07b18fd6617d456c2c469f8e8ed166aadffd
2022-07-07 06:01:57 -07:00
|
|
|
(md[i].shared ? "" : objName) + "_" + mapNames[i];
|
2021-07-05 15:20:34 -07:00
|
|
|
bool reuse = false;
|
|
|
|
|
unique_fd fd;
|
|
|
|
|
int saved_errno;
|
|
|
|
|
|
2018-10-16 13:19:58 -07:00
|
|
|
if (access(mapPinLoc.c_str(), F_OK) == 0) {
|
use mapRetrieveRO() instead of bpf_obj_get()
bpf_obj_get(path) is entirely equivalent to mapRetrieveRW(path)
See implementation in frameworks/libs/net common/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h:
inline int bpfFdGet(const char* pathname, uint32_t flag) {
return bpf(BPF_OBJ_GET, {
.pathname = ptr_to_u64(pathname),
.file_flags = flag,
});
}
inline int mapRetrieve(const char* pathname, uint32_t flag) { return bpfFdGet(pathname, flag); }
inline int mapRetrieveRW(const char* pathname) { return mapRetrieve(pathname, 0); }
inline int mapRetrieveRO(const char* pathname) { return mapRetrieve(pathname, BPF_F_RDONLY); }
inline int mapRetrieveWO(const char* pathname) { return mapRetrieve(pathname, BPF_F_WRONLY); }
inline int retrieveProgram(const char* pathname) { return bpfFdGet(pathname, BPF_F_RDONLY); }
However, this requires selinux file:write access which bpfloader
currently lacks, ie. we would need:
system/sepolicy private/bpfloader.te:
-allow bpfloader bpffs_type:file { create read rename setattr };
+allow bpfloader bpffs_type:file { create read rename setattr write };
Switching from mapRetrieveRW() to mapRetrieveRO() eliminates this problem.
BpfLoader itself does not need to write to the maps, while bpf program write
access is controlled by a different bit (it is independent of the r/w access
bits of the file descriptor itself).
Verified by re-opening the maps after pinning them.
Bug: 218408035
Bug: 237716689
Test: TreeHugger
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: Ica7ac8ee48d4a73e5f92dbf47cd441c3bfba38cf
2022-07-01 03:21:40 -07:00
|
|
|
fd.reset(mapRetrieveRO(mapPinLoc.c_str()));
|
2021-03-01 23:09:54 -08:00
|
|
|
saved_errno = errno;
|
2024-04-30 22:35:08 -07:00
|
|
|
ALOGV("bpf_create_map reusing map %s, ret: %d", mapNames[i].c_str(), fd.get());
|
2018-10-16 13:19:58 -07:00
|
|
|
reuse = true;
|
|
|
|
|
} else {
|
2023-06-12 23:10:52 -07:00
|
|
|
union bpf_attr req = {
|
|
|
|
|
.map_type = type,
|
|
|
|
|
.key_size = md[i].key_size,
|
|
|
|
|
.value_size = md[i].value_size,
|
|
|
|
|
.max_entries = max_entries,
|
|
|
|
|
.map_flags = md[i].map_flags,
|
2022-01-18 21:41:16 -08:00
|
|
|
};
|
2023-06-12 23:10:52 -07:00
|
|
|
strlcpy(req.map_name, mapNames[i].c_str(), sizeof(req.map_name));
|
|
|
|
|
fd.reset(bpf(BPF_MAP_CREATE, req));
|
2021-03-01 23:09:54 -08:00
|
|
|
saved_errno = errno;
|
2024-04-30 22:35:08 -07:00
|
|
|
ALOGV("bpf_create_map name %s, ret: %d", mapNames[i].c_str(), fd.get());
|
2018-10-16 13:19:58 -07:00
|
|
|
}
|
|
|
|
|
|
2022-06-16 15:50:58 -07:00
|
|
|
if (!fd.ok()) return -saved_errno;
|
|
|
|
|
|
|
|
|
|
// When reusing a pinned map, we need to check the map type/sizes/etc match, but for
|
|
|
|
|
// safety (since reuse code path is rare) run these checks even if we just created it.
|
|
|
|
|
// We assume failure is due to pinned map mismatch, hence the 'NOT UNIQUE' return code.
|
|
|
|
|
if (!mapMatchesExpectations(fd, mapNames[i], md[i], type)) return -ENOTUNIQ;
|
2018-10-16 13:19:58 -07:00
|
|
|
|
|
|
|
|
if (!reuse) {
|
2024-03-10 19:11:58 -07:00
|
|
|
ret = bpfFdPin(fd, mapPinLoc.c_str());
|
|
|
|
|
if (ret) {
|
|
|
|
|
int err = errno;
|
|
|
|
|
ALOGE("pin %s -> %d [%d:%s]", mapPinLoc.c_str(), ret, err, strerror(err));
|
|
|
|
|
return -err;
|
2022-06-03 08:41:04 -07:00
|
|
|
}
|
2020-01-27 03:11:51 -08:00
|
|
|
ret = chmod(mapPinLoc.c_str(), md[i].mode);
|
2022-06-03 08:41:04 -07:00
|
|
|
if (ret) {
|
|
|
|
|
int err = errno;
|
|
|
|
|
ALOGE("chmod(%s, 0%o) = %d [%d:%s]", mapPinLoc.c_str(), md[i].mode, ret, err,
|
|
|
|
|
strerror(err));
|
|
|
|
|
return -err;
|
|
|
|
|
}
|
2022-06-27 01:15:53 -07:00
|
|
|
ret = chown(mapPinLoc.c_str(), (uid_t)md[i].uid, (gid_t)md[i].gid);
|
|
|
|
|
if (ret) {
|
|
|
|
|
int err = errno;
|
|
|
|
|
ALOGE("chown(%s, %u, %u) = %d [%d:%s]", mapPinLoc.c_str(), md[i].uid, md[i].gid,
|
|
|
|
|
ret, err, strerror(err));
|
|
|
|
|
return -err;
|
|
|
|
|
}
|
2018-10-16 13:19:58 -07:00
|
|
|
}
|
|
|
|
|
|
2023-06-12 23:10:52 -07:00
|
|
|
int mapId = bpfGetFdMapId(fd);
|
|
|
|
|
if (mapId == -1) {
|
|
|
|
|
ALOGE("bpfGetFdMapId failed, ret: %d [%d]", mapId, errno);
|
2022-05-20 16:44:06 -07:00
|
|
|
} else {
|
2024-04-30 22:35:08 -07:00
|
|
|
ALOGD("map %s id %d", mapPinLoc.c_str(), mapId);
|
2022-05-20 16:44:06 -07:00
|
|
|
}
|
|
|
|
|
|
2019-10-24 18:23:49 -07:00
|
|
|
mapFds.push_back(std::move(fd));
|
2018-10-16 13:19:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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];
|
|
|
|
|
|
2023-03-07 01:11:20 +00:00
|
|
|
// Occasionally might be useful for relocation debugging, but pretty spammy
|
|
|
|
|
if (0) {
|
2024-04-30 22:35:08 -07:00
|
|
|
ALOGV("applying relo to instruction at byte offset: %llu, "
|
2023-03-07 01:11:20 +00:00
|
|
|
"insn offset %d, insn %llx",
|
|
|
|
|
(unsigned long long)offset, insnIndex, *(unsigned long long*)insn);
|
|
|
|
|
}
|
2018-10-16 13:19:58 -07:00
|
|
|
|
|
|
|
|
if (insn->code != (BPF_LD | BPF_IMM | BPF_DW)) {
|
2022-06-17 02:35:36 -07:00
|
|
|
ALOGE("invalid relo for insn %d: code 0x%x", insnIndex, insn->code);
|
2018-10-16 13:19:58 -07:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
insn->imm = fd;
|
|
|
|
|
insn->src_reg = BPF_PSEUDO_MAP_FD;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-24 18:23:49 -07:00
|
|
|
static void applyMapRelo(ifstream& elfFile, vector<unique_fd> &mapFds, vector<codeSection>& cs) {
|
2018-10-16 13:19:58 -07:00
|
|
|
vector<string> mapNames;
|
|
|
|
|
|
2020-02-13 21:45:22 -08:00
|
|
|
int ret = getSectionSymNames(elfFile, "maps", mapNames);
|
2018-10-16 13:19:58 -07:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-14 23:36:32 -08:00
|
|
|
static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const string& license,
|
2024-03-10 19:11:58 -07:00
|
|
|
const char* prefix) {
|
2020-02-19 14:23:59 -08:00
|
|
|
unsigned kvers = kernelVersion();
|
2018-10-16 13:19:58 -07:00
|
|
|
|
2022-08-03 23:49:08 +00:00
|
|
|
if (!kvers) {
|
|
|
|
|
ALOGE("unable to get kernel version");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2018-10-16 13:19:58 -07:00
|
|
|
|
BpfLoader v0.25 - add support for obj@ver.o
This allows for 2 different versions of obj.o to be
shipped simultaneously (via mainline module),
with different bpfloader version limitations.
For example a obj.o for bpfloader < 0.25 and a
obj@25.o for bpfloader 0.25+. These can provide
for different implementations of maps/programs,
while still being pinned into the same ultimate
destination in /sys/fs/bpf/.../{map,prog}_obj_...
so as to not require special selection of appropriate
program/map path names in higher level code
(at least for common functionality).
When using this functionality one does have to be
careful to not end up with unintentional duplication
(ie. an obj@1.o and obj@2.o that both load on bpfloader
version X), and to make sure that the defined
bpf maps and programs with identical names
are also sufficiently identical in behaviour.
In practice it is likely that all versions of obj@ver.o
will be built from the same source code, with compilation
controlled by appropriate preprocessor conditional macros,
to hide certain parts of obj.c while building the version
for older bpfloader...
However, exactly how to use this is ultimately
left up to the future...
Multiple viable mechanisms exist:
(a) each obj@ver.o is standalone, only one should be loaded,
bpfloader min/max version annotations would be used
to guarantee this, making sure that programs/maps that
exist in multiple versions of obj.o should have matching
types and behaviours, but nothing guarantees this
(although key/value size checks will certainly help)
(b) obj.o is baseline and always loaded,
while obj@25.o is an extension with extra maps/programs
and is loaded on only newer bpfloaders,
it may have duplicate defines of shared maps
(likely via #include of some shared header file)
if some of the extra programs also need some of
the data from maps from the 'older' obj.o
(c) variously complex combinations of (a) and (b) are also possible
Bug: 218408035
Test: TreeHugger, manually with offload@1.o in p/m/C
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: Ib60d07b18fd6617d456c2c469f8e8ed166aadffd
2022-07-07 06:01:57 -07:00
|
|
|
string objName = pathToObjName(string(elfPath));
|
2018-10-16 13:19:58 -07:00
|
|
|
|
|
|
|
|
for (int i = 0; i < (int)cs.size(); i++) {
|
2023-06-12 23:10:52 -07:00
|
|
|
unique_fd& fd = cs[i].prog_fd;
|
|
|
|
|
int ret;
|
2020-04-21 15:34:18 -07:00
|
|
|
string name = cs[i].name;
|
2018-10-16 13:19:58 -07:00
|
|
|
|
2022-08-03 23:49:08 +00:00
|
|
|
if (!cs[i].prog_def.has_value()) {
|
|
|
|
|
ALOGE("[%d] '%s' missing program definition! bad bpf.o build?", i, name.c_str());
|
|
|
|
|
return -EINVAL;
|
2020-02-19 14:23:59 -08:00
|
|
|
}
|
|
|
|
|
|
2022-08-03 23:49:08 +00:00
|
|
|
unsigned min_kver = cs[i].prog_def->min_kver;
|
|
|
|
|
unsigned max_kver = cs[i].prog_def->max_kver;
|
2024-04-30 22:35:08 -07:00
|
|
|
if (kvers < min_kver || kvers >= max_kver) {
|
|
|
|
|
ALOGD("skipping program cs[%d].name:%s min_kver:%x max_kver:%x (kvers:%x)",
|
|
|
|
|
i, name.c_str(), min_kver, max_kver, kvers);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2022-08-03 23:49:08 +00:00
|
|
|
|
2020-04-21 15:34:18 -07:00
|
|
|
// strip any potential $foo suffix
|
|
|
|
|
// this can be used to provide duplicate programs
|
|
|
|
|
// conditionally loaded based on running kernel version
|
2020-04-23 12:43:44 -07:00
|
|
|
name = name.substr(0, name.find_last_of('$'));
|
2020-04-21 15:34:18 -07:00
|
|
|
|
|
|
|
|
bool reuse = false;
|
2018-10-16 13:19:58 -07:00
|
|
|
// Format of pin location is
|
BpfLoader v0.25 - add support for obj@ver.o
This allows for 2 different versions of obj.o to be
shipped simultaneously (via mainline module),
with different bpfloader version limitations.
For example a obj.o for bpfloader < 0.25 and a
obj@25.o for bpfloader 0.25+. These can provide
for different implementations of maps/programs,
while still being pinned into the same ultimate
destination in /sys/fs/bpf/.../{map,prog}_obj_...
so as to not require special selection of appropriate
program/map path names in higher level code
(at least for common functionality).
When using this functionality one does have to be
careful to not end up with unintentional duplication
(ie. an obj@1.o and obj@2.o that both load on bpfloader
version X), and to make sure that the defined
bpf maps and programs with identical names
are also sufficiently identical in behaviour.
In practice it is likely that all versions of obj@ver.o
will be built from the same source code, with compilation
controlled by appropriate preprocessor conditional macros,
to hide certain parts of obj.c while building the version
for older bpfloader...
However, exactly how to use this is ultimately
left up to the future...
Multiple viable mechanisms exist:
(a) each obj@ver.o is standalone, only one should be loaded,
bpfloader min/max version annotations would be used
to guarantee this, making sure that programs/maps that
exist in multiple versions of obj.o should have matching
types and behaviours, but nothing guarantees this
(although key/value size checks will certainly help)
(b) obj.o is baseline and always loaded,
while obj@25.o is an extension with extra maps/programs
and is loaded on only newer bpfloaders,
it may have duplicate defines of shared maps
(likely via #include of some shared header file)
if some of the extra programs also need some of
the data from maps from the 'older' obj.o
(c) variously complex combinations of (a) and (b) are also possible
Bug: 218408035
Test: TreeHugger, manually with offload@1.o in p/m/C
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: Ib60d07b18fd6617d456c2c469f8e8ed166aadffd
2022-07-07 06:01:57 -07:00
|
|
|
// /sys/fs/bpf/<prefix>prog_<objName>_<progName>
|
2024-03-10 19:11:58 -07:00
|
|
|
string progPinLoc = string(BPF_FS_PATH) + prefix + "prog_" +
|
BpfLoader v0.25 - add support for obj@ver.o
This allows for 2 different versions of obj.o to be
shipped simultaneously (via mainline module),
with different bpfloader version limitations.
For example a obj.o for bpfloader < 0.25 and a
obj@25.o for bpfloader 0.25+. These can provide
for different implementations of maps/programs,
while still being pinned into the same ultimate
destination in /sys/fs/bpf/.../{map,prog}_obj_...
so as to not require special selection of appropriate
program/map path names in higher level code
(at least for common functionality).
When using this functionality one does have to be
careful to not end up with unintentional duplication
(ie. an obj@1.o and obj@2.o that both load on bpfloader
version X), and to make sure that the defined
bpf maps and programs with identical names
are also sufficiently identical in behaviour.
In practice it is likely that all versions of obj@ver.o
will be built from the same source code, with compilation
controlled by appropriate preprocessor conditional macros,
to hide certain parts of obj.c while building the version
for older bpfloader...
However, exactly how to use this is ultimately
left up to the future...
Multiple viable mechanisms exist:
(a) each obj@ver.o is standalone, only one should be loaded,
bpfloader min/max version annotations would be used
to guarantee this, making sure that programs/maps that
exist in multiple versions of obj.o should have matching
types and behaviours, but nothing guarantees this
(although key/value size checks will certainly help)
(b) obj.o is baseline and always loaded,
while obj@25.o is an extension with extra maps/programs
and is loaded on only newer bpfloaders,
it may have duplicate defines of shared maps
(likely via #include of some shared header file)
if some of the extra programs also need some of
the data from maps from the 'older' obj.o
(c) variously complex combinations of (a) and (b) are also possible
Bug: 218408035
Test: TreeHugger, manually with offload@1.o in p/m/C
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: Ib60d07b18fd6617d456c2c469f8e8ed166aadffd
2022-07-07 06:01:57 -07:00
|
|
|
objName + '_' + string(name);
|
2018-10-16 13:19:58 -07:00
|
|
|
if (access(progPinLoc.c_str(), F_OK) == 0) {
|
2023-06-12 23:10:52 -07:00
|
|
|
fd.reset(retrieveProgram(progPinLoc.c_str()));
|
2024-04-30 22:35:08 -07:00
|
|
|
ALOGV("New bpf prog load reusing prog %s, ret: %d (%s)", progPinLoc.c_str(), fd.get(),
|
2023-06-12 23:10:52 -07:00
|
|
|
(!fd.ok() ? std::strerror(errno) : "no error"));
|
2018-10-16 13:19:58 -07:00
|
|
|
reuse = true;
|
|
|
|
|
} else {
|
|
|
|
|
vector<char> log_buf(BPF_LOAD_LOG_SZ, 0);
|
|
|
|
|
|
2023-06-12 23:10:52 -07:00
|
|
|
union bpf_attr req = {
|
|
|
|
|
.prog_type = cs[i].type,
|
|
|
|
|
.kern_version = kvers,
|
|
|
|
|
.license = ptr_to_u64(license.c_str()),
|
|
|
|
|
.insns = ptr_to_u64(cs[i].data.data()),
|
|
|
|
|
.insn_cnt = static_cast<__u32>(cs[i].data.size() / sizeof(struct bpf_insn)),
|
|
|
|
|
.log_level = 1,
|
|
|
|
|
.log_buf = ptr_to_u64(log_buf.data()),
|
|
|
|
|
.log_size = static_cast<__u32>(log_buf.size()),
|
|
|
|
|
.expected_attach_type = cs[i].expected_attach_type,
|
2022-02-03 09:46:01 -08:00
|
|
|
};
|
2023-06-12 23:10:52 -07:00
|
|
|
strlcpy(req.prog_name, cs[i].name.c_str(), sizeof(req.prog_name));
|
|
|
|
|
fd.reset(bpf(BPF_PROG_LOAD, req));
|
2022-02-03 09:46:01 -08:00
|
|
|
|
2023-06-12 23:10:52 -07:00
|
|
|
if (!fd.ok()) {
|
2024-04-30 22:35:08 -07:00
|
|
|
ALOGW("BPF_PROG_LOAD call for %s (%s) returned fd: %d (%s)", elfPath,
|
|
|
|
|
cs[i].name.c_str(), fd.get(), std::strerror(errno));
|
|
|
|
|
|
2021-01-21 16:01:04 -08:00
|
|
|
vector<string> lines = android::base::Split(log_buf.data(), "\n");
|
2020-02-11 11:12:37 -08:00
|
|
|
|
2023-06-12 23:10:52 -07:00
|
|
|
ALOGW("BPF_PROG_LOAD - BEGIN log_buf contents:");
|
implement support for functions which may optionally fail to load
This is useful for critical functions with fallbacks, but
may even be useful for non-critical functions, where a function
in the middle of the file may fail to load, but you still want
other (later) functions to be attempted.
Critical applies to the entire .c file (or to be more correct to
the entire resulting .o). Optional applies to a specific section
of that .o (ie. a specific individual function).
This new optional attribute is necessary to be able to declare
a .c/.o file critical even if *some* of the individual functions
might fail to load due to missing kernel patches.
(Note: we currently have no way to specify a map as optional)
Critical guarantees that all non-optional programs, and all maps,
have been created, pinned, chowned, and chmoded successfully
(or that they already existed).
For an example of use see:
system/netd/bpf_progs/offload.c
(while at it also add retrieveProgram() and mapRetrieve{RW,RO,WO}()
helpers to BpfUtils.h)
Test: builds, atest, see paired netd change for extra details
Bug: 150040815
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: I50b292c061b05fc8f4b4b8574f128345c45c78db
2020-06-16 17:02:48 -07:00
|
|
|
for (const auto& line : lines) ALOGW("%s", line.c_str());
|
2023-06-12 23:10:52 -07:00
|
|
|
ALOGW("BPF_PROG_LOAD - END log_buf contents.");
|
implement support for functions which may optionally fail to load
This is useful for critical functions with fallbacks, but
may even be useful for non-critical functions, where a function
in the middle of the file may fail to load, but you still want
other (later) functions to be attempted.
Critical applies to the entire .c file (or to be more correct to
the entire resulting .o). Optional applies to a specific section
of that .o (ie. a specific individual function).
This new optional attribute is necessary to be able to declare
a .c/.o file critical even if *some* of the individual functions
might fail to load due to missing kernel patches.
(Note: we currently have no way to specify a map as optional)
Critical guarantees that all non-optional programs, and all maps,
have been created, pinned, chowned, and chmoded successfully
(or that they already existed).
For an example of use see:
system/netd/bpf_progs/offload.c
(while at it also add retrieveProgram() and mapRetrieve{RW,RO,WO}()
helpers to BpfUtils.h)
Test: builds, atest, see paired netd change for extra details
Bug: 150040815
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: I50b292c061b05fc8f4b4b8574f128345c45c78db
2020-06-16 17:02:48 -07:00
|
|
|
|
2022-08-03 23:49:08 +00:00
|
|
|
if (cs[i].prog_def->optional) {
|
implement support for functions which may optionally fail to load
This is useful for critical functions with fallbacks, but
may even be useful for non-critical functions, where a function
in the middle of the file may fail to load, but you still want
other (later) functions to be attempted.
Critical applies to the entire .c file (or to be more correct to
the entire resulting .o). Optional applies to a specific section
of that .o (ie. a specific individual function).
This new optional attribute is necessary to be able to declare
a .c/.o file critical even if *some* of the individual functions
might fail to load due to missing kernel patches.
(Note: we currently have no way to specify a map as optional)
Critical guarantees that all non-optional programs, and all maps,
have been created, pinned, chowned, and chmoded successfully
(or that they already existed).
For an example of use see:
system/netd/bpf_progs/offload.c
(while at it also add retrieveProgram() and mapRetrieve{RW,RO,WO}()
helpers to BpfUtils.h)
Test: builds, atest, see paired netd change for extra details
Bug: 150040815
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: I50b292c061b05fc8f4b4b8574f128345c45c78db
2020-06-16 17:02:48 -07:00
|
|
|
ALOGW("failed program is marked optional - continuing...");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
ALOGE("non-optional program failed to load.");
|
2020-02-11 11:12:37 -08:00
|
|
|
}
|
2018-10-16 13:19:58 -07:00
|
|
|
}
|
|
|
|
|
|
2023-06-12 23:10:52 -07:00
|
|
|
if (!fd.ok()) return fd.get();
|
2018-10-16 13:19:58 -07:00
|
|
|
|
|
|
|
|
if (!reuse) {
|
2024-03-10 19:11:58 -07:00
|
|
|
ret = bpfFdPin(fd, progPinLoc.c_str());
|
|
|
|
|
if (ret) {
|
|
|
|
|
int err = errno;
|
|
|
|
|
ALOGE("create %s -> %d [%d:%s]", progPinLoc.c_str(), ret, err, strerror(err));
|
|
|
|
|
return -err;
|
2022-06-03 08:41:04 -07:00
|
|
|
}
|
|
|
|
|
if (chmod(progPinLoc.c_str(), 0440)) {
|
|
|
|
|
int err = errno;
|
|
|
|
|
ALOGE("chmod %s 0440 -> [%d:%s]", progPinLoc.c_str(), err, strerror(err));
|
|
|
|
|
return -err;
|
|
|
|
|
}
|
2022-08-03 23:49:08 +00:00
|
|
|
if (chown(progPinLoc.c_str(), (uid_t)cs[i].prog_def->uid,
|
|
|
|
|
(gid_t)cs[i].prog_def->gid)) {
|
|
|
|
|
int err = errno;
|
|
|
|
|
ALOGE("chown %s %d %d -> [%d:%s]", progPinLoc.c_str(), cs[i].prog_def->uid,
|
|
|
|
|
cs[i].prog_def->gid, err, strerror(err));
|
|
|
|
|
return -err;
|
2020-02-13 21:45:22 -08:00
|
|
|
}
|
2018-10-16 13:19:58 -07:00
|
|
|
}
|
|
|
|
|
|
2023-06-12 23:10:52 -07:00
|
|
|
int progId = bpfGetFdProgId(fd);
|
|
|
|
|
if (progId == -1) {
|
|
|
|
|
ALOGE("bpfGetFdProgId failed, ret: %d [%d]", progId, errno);
|
2022-05-20 16:44:06 -07:00
|
|
|
} else {
|
2024-04-30 22:35:08 -07:00
|
|
|
ALOGD("prog %s id %d", progPinLoc.c_str(), progId);
|
2022-05-20 16:44:06 -07:00
|
|
|
}
|
2018-10-16 13:19:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-01 20:08:17 -08:00
|
|
|
int loadProg(const char* elfPath, bool* isCritical, const Location& location) {
|
2018-10-16 13:19:58 -07:00
|
|
|
vector<char> license;
|
2020-06-10 15:49:31 -07:00
|
|
|
vector<char> critical;
|
2018-10-16 13:19:58 -07:00
|
|
|
vector<codeSection> cs;
|
2019-10-24 18:23:49 -07:00
|
|
|
vector<unique_fd> mapFds;
|
2018-10-16 13:19:58 -07:00
|
|
|
int ret;
|
|
|
|
|
|
2020-06-14 19:27:33 -07:00
|
|
|
if (!isCritical) return -1;
|
|
|
|
|
*isCritical = false;
|
|
|
|
|
|
2018-10-16 13:19:58 -07:00
|
|
|
ifstream elfFile(elfPath, ios::in | ios::binary);
|
|
|
|
|
if (!elfFile.is_open()) return -1;
|
|
|
|
|
|
2020-06-10 15:49:31 -07:00
|
|
|
ret = readSectionByName("critical", elfFile, critical);
|
2020-06-14 19:27:33 -07:00
|
|
|
*isCritical = !ret;
|
2020-06-10 15:49:31 -07:00
|
|
|
|
2018-10-16 13:19:58 -07:00
|
|
|
ret = readSectionByName("license", elfFile, license);
|
|
|
|
|
if (ret) {
|
2022-06-17 02:35:36 -07:00
|
|
|
ALOGE("Couldn't find license in %s", elfPath);
|
2018-10-16 13:19:58 -07:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-30 22:35:08 -07:00
|
|
|
ALOGI("Platform BpfLoader loading %s%s ELF object %s with license %s",
|
|
|
|
|
*isCritical ? "critical for " : "optional", *isCritical ? (char*)critical.data() : "",
|
|
|
|
|
elfPath, (char*)license.data());
|
2021-03-03 05:28:52 -08:00
|
|
|
|
2024-03-10 14:37:24 -07:00
|
|
|
ret = readCodeSections(elfFile, cs, location.allowedProgTypes, location.allowedProgTypesLength);
|
2018-10-16 13:19:58 -07:00
|
|
|
if (ret) {
|
2022-06-17 02:35:36 -07:00
|
|
|
ALOGE("Couldn't read all code sections in %s", elfPath);
|
2018-10-16 13:19:58 -07:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-10 19:11:58 -07:00
|
|
|
ret = createMaps(elfPath, elfFile, mapFds, location.prefix);
|
2018-10-16 13:19:58 -07:00
|
|
|
if (ret) {
|
2022-06-17 02:35:36 -07:00
|
|
|
ALOGE("Failed to create maps: (ret=%d) in %s", ret, elfPath);
|
2018-10-16 13:19:58 -07:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < (int)mapFds.size(); i++)
|
2024-04-30 22:35:08 -07:00
|
|
|
ALOGV("map_fd found at %d is %d in %s", i, mapFds[i].get(), elfPath);
|
2018-10-16 13:19:58 -07:00
|
|
|
|
|
|
|
|
applyMapRelo(elfFile, mapFds, cs);
|
|
|
|
|
|
2024-03-10 19:11:58 -07:00
|
|
|
ret = loadCodeSections(elfPath, cs, string(license.data()), location.prefix);
|
2022-06-17 02:35:36 -07:00
|
|
|
if (ret) ALOGE("Failed to load programs, loadCodeSections ret=%d", ret);
|
2018-10-16 13:19:58 -07:00
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-06 15:25:55 -07:00
|
|
|
// Networking-related program types are limited to the Tethering Apex
|
|
|
|
|
// to prevent things from breaking due to conflicts on mainline updates
|
|
|
|
|
// (exception made for socket filters, ie. xt_bpf for potential use in iptables,
|
|
|
|
|
// or for attaching to sockets directly)
|
|
|
|
|
constexpr bpf_prog_type kPlatformAllowedProgTypes[] = {
|
|
|
|
|
BPF_PROG_TYPE_KPROBE,
|
|
|
|
|
BPF_PROG_TYPE_PERF_EVENT,
|
|
|
|
|
BPF_PROG_TYPE_SOCKET_FILTER,
|
|
|
|
|
BPF_PROG_TYPE_TRACEPOINT,
|
|
|
|
|
BPF_PROG_TYPE_UNSPEC, // Will be replaced with fuse bpf program type
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
constexpr bpf_prog_type kUprobestatsAllowedProgTypes[] = {
|
|
|
|
|
BPF_PROG_TYPE_KPROBE,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// see b/162057235. For arbitrary program types, the concern is that due to the lack of
|
|
|
|
|
// SELinux access controls over BPF program attachpoints, we have no way to control the
|
|
|
|
|
// attachment of programs to shared resources (or to detect when a shared resource
|
|
|
|
|
// has one BPF program replace another that is attached there)
|
|
|
|
|
constexpr bpf_prog_type kVendorAllowedProgTypes[] = {
|
|
|
|
|
BPF_PROG_TYPE_SOCKET_FILTER,
|
|
|
|
|
};
|
|
|
|
|
|
2024-08-06 15:32:00 -07:00
|
|
|
const Location locations[] = {
|
2024-08-06 15:25:55 -07:00
|
|
|
// Core operating system
|
|
|
|
|
{
|
|
|
|
|
.dir = "/system/etc/bpf/",
|
|
|
|
|
.prefix = "",
|
|
|
|
|
.allowedProgTypes = kPlatformAllowedProgTypes,
|
|
|
|
|
.allowedProgTypesLength = arraysize(kPlatformAllowedProgTypes),
|
|
|
|
|
},
|
|
|
|
|
// uprobestats
|
|
|
|
|
{
|
|
|
|
|
.dir = "/system/etc/bpf/uprobestats/",
|
|
|
|
|
.prefix = "uprobestats/",
|
|
|
|
|
.allowedProgTypes = kUprobestatsAllowedProgTypes,
|
|
|
|
|
.allowedProgTypesLength = arraysize(kUprobestatsAllowedProgTypes),
|
|
|
|
|
},
|
|
|
|
|
// Vendor operating system
|
|
|
|
|
{
|
|
|
|
|
.dir = "/vendor/etc/bpf/",
|
|
|
|
|
.prefix = "vendor/",
|
|
|
|
|
.allowedProgTypes = kVendorAllowedProgTypes,
|
|
|
|
|
.allowedProgTypesLength = arraysize(kVendorAllowedProgTypes),
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
2024-08-06 15:32:00 -07:00
|
|
|
int loadAllElfObjects(const Location& location) {
|
2024-08-06 15:25:55 -07:00
|
|
|
int retVal = 0;
|
|
|
|
|
DIR* dir;
|
|
|
|
|
struct dirent* ent;
|
|
|
|
|
|
|
|
|
|
if ((dir = opendir(location.dir)) != NULL) {
|
|
|
|
|
while ((ent = readdir(dir)) != NULL) {
|
|
|
|
|
string s = ent->d_name;
|
|
|
|
|
if (!EndsWith(s, ".o")) continue;
|
|
|
|
|
|
|
|
|
|
string progPath(location.dir);
|
|
|
|
|
progPath += s;
|
|
|
|
|
|
|
|
|
|
bool critical;
|
2024-08-06 15:32:00 -07:00
|
|
|
int ret = loadProg(progPath.c_str(), &critical, location);
|
2024-08-06 15:25:55 -07:00
|
|
|
if (ret) {
|
|
|
|
|
if (critical) retVal = ret;
|
2024-08-06 15:32:00 -07:00
|
|
|
ALOGE("Failed to load object: %s, ret: %s", progPath.c_str(), strerror(-ret));
|
2024-08-06 15:25:55 -07:00
|
|
|
} else {
|
|
|
|
|
ALOGV("Loaded object: %s", progPath.c_str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
closedir(dir);
|
|
|
|
|
}
|
|
|
|
|
return retVal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int createSysFsBpfSubDir(const char* const prefix) {
|
|
|
|
|
if (*prefix) {
|
|
|
|
|
mode_t prevUmask = umask(0);
|
|
|
|
|
|
|
|
|
|
string s = "/sys/fs/bpf/";
|
|
|
|
|
s += prefix;
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
int ret = mkdir(s.c_str(), S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
|
|
|
|
|
if (ret && errno != EEXIST) {
|
|
|
|
|
const int err = errno;
|
2024-08-06 15:32:00 -07:00
|
|
|
ALOGE("Failed to create directory: %s, ret: %s", s.c_str(), strerror(err));
|
2024-08-06 15:25:55 -07:00
|
|
|
return -err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
umask(prevUmask);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-07 12:27:18 -07:00
|
|
|
} // namespace bpf
|
|
|
|
|
} // namespace android
|
2024-08-06 15:25:55 -07:00
|
|
|
|
2024-08-07 12:27:18 -07:00
|
|
|
// ----- extern C stuff for rust below here -----
|
|
|
|
|
|
|
|
|
|
void initLogging() {
|
|
|
|
|
// since we only ever get called from mainline NetBpfLoad
|
|
|
|
|
// (see packages/modules/Connectivity/netbpfload/NetBpfLoad.cpp around line 516)
|
|
|
|
|
// and there no arguments, so we can just pretend/assume this is the case.
|
|
|
|
|
const char* argv[] = {"/system/bin/bpfloader", NULL};
|
|
|
|
|
android::base::InitLogging(const_cast<char**>(argv), &android::base::KernelLogger);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void legacyBpfLoader() {
|
2024-08-06 15:25:55 -07:00
|
|
|
// Load all ELF objects, create programs and maps, and pin them
|
2024-08-07 12:27:18 -07:00
|
|
|
for (const auto& location : android::bpf::locations) {
|
|
|
|
|
if (android::bpf::createSysFsBpfSubDir(location.prefix) ||
|
|
|
|
|
android::bpf::loadAllElfObjects(location)) {
|
2024-08-06 15:25:55 -07:00
|
|
|
ALOGE("=== CRITICAL FAILURE LOADING BPF PROGRAMS FROM %s ===", location.dir);
|
|
|
|
|
ALOGE("If this triggers reliably, you're probably missing kernel options or patches.");
|
|
|
|
|
ALOGE("If this triggers randomly, you might be hitting some memory allocation "
|
|
|
|
|
"problems or startup script race.");
|
|
|
|
|
ALOGE("--- DO NOT EXPECT SYSTEM TO BOOT SUCCESSFULLY ---");
|
|
|
|
|
sleep(20);
|
2024-08-07 12:27:18 -07:00
|
|
|
exit(120);
|
2024-08-06 15:25:55 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-06 15:32:00 -07:00
|
|
|
|
2024-08-07 12:27:18 -07:00
|
|
|
void execNetBpfLoadDone() {
|
|
|
|
|
const char* args[] = {"/apex/com.android.tethering/bin/netbpfload", "done", NULL};
|
|
|
|
|
execve(args[0], (char**)args, environ);
|
|
|
|
|
ALOGE("FATAL: execve(): %d[%s]", errno, strerror(errno));
|
|
|
|
|
exit(121);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void logVerbose(const char* msg) {
|
|
|
|
|
ALOGV("%s", msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void logDebug(const char* msg) {
|
|
|
|
|
ALOGD("%s", msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void logInfo(const char* msg) {
|
|
|
|
|
ALOGI("%s", msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void logWarn(const char* msg) {
|
|
|
|
|
ALOGW("%s", msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void logError(const char* msg) {
|
|
|
|
|
ALOGE("%s", msg);
|
|
|
|
|
}
|