1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "LibBpfLoader"
18
19 #include <errno.h>
20 #include <linux/bpf.h>
21 #include <linux/elf.h>
22 #include <log/log.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <sys/utsname.h>
29 #include <unistd.h>
30
31 // This is BpfLoader v0.2
32 #define BPFLOADER_VERSION_MAJOR 0u
33 #define BPFLOADER_VERSION_MINOR 2u
34 #define BPFLOADER_VERSION ((BPFLOADER_VERSION_MAJOR << 16) | BPFLOADER_VERSION_MINOR)
35
36 #include "../progs/include/bpf_map_def.h"
37 #include "bpf/BpfUtils.h"
38 #include "include/libbpf_android.h"
39
40 #include <cstdlib>
41 #include <fstream>
42 #include <iostream>
43 #include <optional>
44 #include <string>
45 #include <vector>
46
47 #include <android-base/strings.h>
48 #include <android-base/unique_fd.h>
49
50 #define BPF_FS_PATH "/sys/fs/bpf/"
51
52 // Size of the BPF log buffer for verifier logging
53 #define BPF_LOAD_LOG_SZ 0x1ffff
54
55 using android::base::StartsWith;
56 using android::base::unique_fd;
57 using std::ifstream;
58 using std::ios;
59 using std::optional;
60 using std::string;
61 using std::vector;
62
63 namespace android {
64 namespace bpf {
65
pathToFilename(const string & path,bool noext=false)66 static string pathToFilename(const string& path, bool noext = false) {
67 vector<string> spath = android::base::Split(path, "/");
68 string ret = spath.back();
69
70 if (noext) {
71 size_t lastindex = ret.find_last_of('.');
72 return ret.substr(0, lastindex);
73 }
74 return ret;
75 }
76
77 typedef struct {
78 const char* name;
79 enum bpf_prog_type type;
80 } sectionType;
81
82 /*
83 * Map section name prefixes to program types, the section name will be:
84 * SEC(<prefix>/<name-of-program>)
85 * For example:
86 * SEC("tracepoint/sched_switch_func") where sched_switch_funcs
87 * is the name of the program, and tracepoint is the type.
88 */
89 sectionType sectionNameTypes[] = {
90 {"kprobe", BPF_PROG_TYPE_KPROBE},
91 {"tracepoint", BPF_PROG_TYPE_TRACEPOINT},
92 {"skfilter", BPF_PROG_TYPE_SOCKET_FILTER},
93 {"cgroupskb", BPF_PROG_TYPE_CGROUP_SKB},
94 {"schedcls", BPF_PROG_TYPE_SCHED_CLS},
95 {"cgroupsock", BPF_PROG_TYPE_CGROUP_SOCK},
96 {"xdp", BPF_PROG_TYPE_XDP},
97
98 /* End of table */
99 {"END", BPF_PROG_TYPE_UNSPEC},
100 };
101
102 typedef struct {
103 enum bpf_prog_type type;
104 string name;
105 vector<char> data;
106 vector<char> rel_data;
107 optional<struct bpf_prog_def> prog_def;
108
109 unique_fd prog_fd; /* fd after loading */
110 } codeSection;
111
readElfHeader(ifstream & elfFile,Elf64_Ehdr * eh)112 static int readElfHeader(ifstream& elfFile, Elf64_Ehdr* eh) {
113 elfFile.seekg(0);
114 if (elfFile.fail()) return -1;
115
116 if (!elfFile.read((char*)eh, sizeof(*eh))) return -1;
117
118 return 0;
119 }
120
121 /* Reads all section header tables into an Shdr array */
readSectionHeadersAll(ifstream & elfFile,vector<Elf64_Shdr> & shTable)122 static int readSectionHeadersAll(ifstream& elfFile, vector<Elf64_Shdr>& shTable) {
123 Elf64_Ehdr eh;
124 int ret = 0;
125
126 ret = readElfHeader(elfFile, &eh);
127 if (ret) return ret;
128
129 elfFile.seekg(eh.e_shoff);
130 if (elfFile.fail()) return -1;
131
132 /* Read shdr table entries */
133 shTable.resize(eh.e_shnum);
134
135 if (!elfFile.read((char*)shTable.data(), (eh.e_shnum * eh.e_shentsize))) return -ENOMEM;
136
137 return 0;
138 }
139
140 /* Read a section by its index - for ex to get sec hdr strtab blob */
readSectionByIdx(ifstream & elfFile,int id,vector<char> & sec)141 static int readSectionByIdx(ifstream& elfFile, int id, vector<char>& sec) {
142 vector<Elf64_Shdr> shTable;
143 int ret = readSectionHeadersAll(elfFile, shTable);
144 if (ret) return ret;
145
146 elfFile.seekg(shTable[id].sh_offset);
147 if (elfFile.fail()) return -1;
148
149 sec.resize(shTable[id].sh_size);
150 if (!elfFile.read(sec.data(), shTable[id].sh_size)) return -1;
151
152 return 0;
153 }
154
155 /* Read whole section header string table */
readSectionHeaderStrtab(ifstream & elfFile,vector<char> & strtab)156 static int readSectionHeaderStrtab(ifstream& elfFile, vector<char>& strtab) {
157 Elf64_Ehdr eh;
158 int ret = readElfHeader(elfFile, &eh);
159 if (ret) return ret;
160
161 ret = readSectionByIdx(elfFile, eh.e_shstrndx, strtab);
162 if (ret) return ret;
163
164 return 0;
165 }
166
167 /* Get name from offset in strtab */
getSymName(ifstream & elfFile,int nameOff,string & name)168 static int getSymName(ifstream& elfFile, int nameOff, string& name) {
169 int ret;
170 vector<char> secStrTab;
171
172 ret = readSectionHeaderStrtab(elfFile, secStrTab);
173 if (ret) return ret;
174
175 if (nameOff >= (int)secStrTab.size()) return -1;
176
177 name = string((char*)secStrTab.data() + nameOff);
178 return 0;
179 }
180
181 /* Reads a full section by name - example to get the GPL license */
readSectionByName(const char * name,ifstream & elfFile,vector<char> & data)182 static int readSectionByName(const char* name, ifstream& elfFile, vector<char>& data) {
183 vector<char> secStrTab;
184 vector<Elf64_Shdr> shTable;
185 int ret;
186
187 ret = readSectionHeadersAll(elfFile, shTable);
188 if (ret) return ret;
189
190 ret = readSectionHeaderStrtab(elfFile, secStrTab);
191 if (ret) return ret;
192
193 for (int i = 0; i < (int)shTable.size(); i++) {
194 char* secname = secStrTab.data() + shTable[i].sh_name;
195 if (!secname) continue;
196
197 if (!strcmp(secname, name)) {
198 vector<char> dataTmp;
199 dataTmp.resize(shTable[i].sh_size);
200
201 elfFile.seekg(shTable[i].sh_offset);
202 if (elfFile.fail()) return -1;
203
204 if (!elfFile.read((char*)dataTmp.data(), shTable[i].sh_size)) return -1;
205
206 data = dataTmp;
207 return 0;
208 }
209 }
210 return -2;
211 }
212
readSectionUint(const char * name,ifstream & elfFile,unsigned int defVal)213 unsigned int readSectionUint(const char* name, ifstream& elfFile, unsigned int defVal) {
214 vector<char> theBytes;
215 int ret = readSectionByName(name, elfFile, theBytes);
216 if (ret) {
217 ALOGD("Couldn't find section %s (defaulting to %u [0x%x]).\n", name, defVal, defVal);
218 return defVal;
219 } else if (theBytes.size() < sizeof(unsigned int)) {
220 ALOGE("Section %s too short (defaulting to %u [0x%x]).\n", name, defVal, defVal);
221 return defVal;
222 } else {
223 // decode first 4 bytes as LE32 uint, there will likely be more bytes due to alignment.
224 unsigned int value = static_cast<unsigned char>(theBytes[3]);
225 value <<= 8;
226 value += static_cast<unsigned char>(theBytes[2]);
227 value <<= 8;
228 value += static_cast<unsigned char>(theBytes[1]);
229 value <<= 8;
230 value += static_cast<unsigned char>(theBytes[0]);
231 ALOGI("Section %s value is %u [0x%x]\n", name, value, value);
232 return value;
233 }
234 }
235
readSectionByType(ifstream & elfFile,int type,vector<char> & data)236 static int readSectionByType(ifstream& elfFile, int type, vector<char>& data) {
237 int ret;
238 vector<Elf64_Shdr> shTable;
239
240 ret = readSectionHeadersAll(elfFile, shTable);
241 if (ret) return ret;
242
243 for (int i = 0; i < (int)shTable.size(); i++) {
244 if ((int)shTable[i].sh_type != type) continue;
245
246 vector<char> dataTmp;
247 dataTmp.resize(shTable[i].sh_size);
248
249 elfFile.seekg(shTable[i].sh_offset);
250 if (elfFile.fail()) return -1;
251
252 if (!elfFile.read((char*)dataTmp.data(), shTable[i].sh_size)) return -1;
253
254 data = dataTmp;
255 return 0;
256 }
257 return -2;
258 }
259
symCompare(Elf64_Sym a,Elf64_Sym b)260 static bool symCompare(Elf64_Sym a, Elf64_Sym b) {
261 return (a.st_value < b.st_value);
262 }
263
readSymTab(ifstream & elfFile,int sort,vector<Elf64_Sym> & data)264 static int readSymTab(ifstream& elfFile, int sort, vector<Elf64_Sym>& data) {
265 int ret, numElems;
266 Elf64_Sym* buf;
267 vector<char> secData;
268
269 ret = readSectionByType(elfFile, SHT_SYMTAB, secData);
270 if (ret) return ret;
271
272 buf = (Elf64_Sym*)secData.data();
273 numElems = (secData.size() / sizeof(Elf64_Sym));
274 data.assign(buf, buf + numElems);
275
276 if (sort) std::sort(data.begin(), data.end(), symCompare);
277 return 0;
278 }
279
getSectionType(string & name)280 static enum bpf_prog_type getSectionType(string& name) {
281 for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++)
282 if (StartsWith(name, sectionNameTypes[i].name)) return sectionNameTypes[i].type;
283
284 return BPF_PROG_TYPE_UNSPEC;
285 }
286
287 /* If ever needed
288 static string getSectionName(enum bpf_prog_type type)
289 {
290 for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++)
291 if (sectionNameTypes[i].type == type)
292 return string(sectionNameTypes[i].name);
293
294 return NULL;
295 }
296 */
297
isRelSection(codeSection & cs,string & name)298 static bool isRelSection(codeSection& cs, string& name) {
299 for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++) {
300 sectionType st = sectionNameTypes[i];
301
302 if (st.type != cs.type) continue;
303
304 if (StartsWith(name, string(".rel") + st.name + "/"))
305 return true;
306 else
307 return false;
308 }
309 return false;
310 }
311
readProgDefs(ifstream & elfFile,vector<struct bpf_prog_def> & pd,size_t sizeOfBpfProgDef)312 static int readProgDefs(ifstream& elfFile, vector<struct bpf_prog_def>& pd,
313 size_t sizeOfBpfProgDef) {
314 vector<char> pdData;
315 int ret = readSectionByName("progs", elfFile, pdData);
316 // Older file formats do not require a 'progs' section at all.
317 // (We should probably figure out whether this is behaviour which is safe to remove now.)
318 if (ret == -2) return 0;
319 if (ret) return ret;
320
321 if (pdData.size() % sizeOfBpfProgDef) {
322 ALOGE("readProgDefs failed due to improper sized progs section, %zu %% %zu != 0\n",
323 pdData.size(), sizeOfBpfProgDef);
324 return -1;
325 };
326
327 int progCount = pdData.size() / sizeOfBpfProgDef;
328 pd.resize(progCount);
329 size_t trimmedSize = std::min(sizeOfBpfProgDef, sizeof(struct bpf_prog_def));
330
331 const char* dataPtr = pdData.data();
332 for (auto& p : pd) {
333 // First we zero initialize
334 memset(&p, 0, sizeof(p));
335 // Then we set non-zero defaults
336 p.bpfloader_max_ver = DEFAULT_BPFLOADER_MAX_VER; // v1.0
337 // Then we copy over the structure prefix from the ELF file.
338 memcpy(&p, dataPtr, trimmedSize);
339 // Move to next struct in the ELF file
340 dataPtr += sizeOfBpfProgDef;
341 }
342 return 0;
343 }
344
getSectionSymNames(ifstream & elfFile,const string & sectionName,vector<string> & names)345 static int getSectionSymNames(ifstream& elfFile, const string& sectionName, vector<string>& names) {
346 int ret;
347 string name;
348 vector<Elf64_Sym> symtab;
349 vector<Elf64_Shdr> shTable;
350
351 ret = readSymTab(elfFile, 1 /* sort */, symtab);
352 if (ret) return ret;
353
354 /* Get index of section */
355 ret = readSectionHeadersAll(elfFile, shTable);
356 if (ret) return ret;
357
358 int sec_idx = -1;
359 for (int i = 0; i < (int)shTable.size(); i++) {
360 ret = getSymName(elfFile, shTable[i].sh_name, name);
361 if (ret) return ret;
362
363 if (!name.compare(sectionName)) {
364 sec_idx = i;
365 break;
366 }
367 }
368
369 /* No section found with matching name*/
370 if (sec_idx == -1) {
371 ALOGW("No %s section could be found in elf object\n", sectionName.c_str());
372 return -1;
373 }
374
375 for (int i = 0; i < (int)symtab.size(); i++) {
376 if (symtab[i].st_shndx == sec_idx) {
377 string s;
378 ret = getSymName(elfFile, symtab[i].st_name, s);
379 if (ret) return ret;
380 names.push_back(s);
381 }
382 }
383
384 return 0;
385 }
386
387 /* Read a section by its index - for ex to get sec hdr strtab blob */
readCodeSections(ifstream & elfFile,vector<codeSection> & cs,size_t sizeOfBpfProgDef)388 static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs, size_t sizeOfBpfProgDef) {
389 vector<Elf64_Shdr> shTable;
390 int entries, ret = 0;
391
392 ret = readSectionHeadersAll(elfFile, shTable);
393 if (ret) return ret;
394 entries = shTable.size();
395
396 vector<struct bpf_prog_def> pd;
397 ret = readProgDefs(elfFile, pd, sizeOfBpfProgDef);
398 if (ret) return ret;
399 vector<string> progDefNames;
400 ret = getSectionSymNames(elfFile, "progs", progDefNames);
401 if (!pd.empty() && ret) return ret;
402
403 for (int i = 0; i < entries; i++) {
404 string name;
405 codeSection cs_temp;
406 cs_temp.type = BPF_PROG_TYPE_UNSPEC;
407
408 ret = getSymName(elfFile, shTable[i].sh_name, name);
409 if (ret) return ret;
410
411 enum bpf_prog_type ptype = getSectionType(name);
412 if (ptype != BPF_PROG_TYPE_UNSPEC) {
413 string oldName = name;
414
415 // convert all slashes to underscores
416 std::replace(name.begin(), name.end(), '/', '_');
417
418 cs_temp.type = ptype;
419 cs_temp.name = name;
420
421 ret = readSectionByIdx(elfFile, i, cs_temp.data);
422 if (ret) return ret;
423 ALOGD("Loaded code section %d (%s)\n", i, name.c_str());
424
425 vector<string> csSymNames;
426 ret = getSectionSymNames(elfFile, oldName, csSymNames);
427 if (ret || !csSymNames.size()) return ret;
428 for (size_t i = 0; i < progDefNames.size(); ++i) {
429 if (!progDefNames[i].compare(csSymNames[0] + "_def")) {
430 cs_temp.prog_def = pd[i];
431 break;
432 }
433 }
434 }
435
436 /* Check for rel section */
437 if (cs_temp.data.size() > 0 && i < entries) {
438 ret = getSymName(elfFile, shTable[i + 1].sh_name, name);
439 if (ret) return ret;
440
441 if (isRelSection(cs_temp, name)) {
442 ret = readSectionByIdx(elfFile, i + 1, cs_temp.rel_data);
443 if (ret) return ret;
444 ALOGD("Loaded relo section %d (%s)\n", i, name.c_str());
445 }
446 }
447
448 if (cs_temp.data.size() > 0) {
449 cs.push_back(std::move(cs_temp));
450 ALOGD("Adding section %d to cs list\n", i);
451 }
452 }
453 return 0;
454 }
455
getSymNameByIdx(ifstream & elfFile,int index,string & name)456 static int getSymNameByIdx(ifstream& elfFile, int index, string& name) {
457 vector<Elf64_Sym> symtab;
458 int ret = 0;
459
460 ret = readSymTab(elfFile, 0 /* !sort */, symtab);
461 if (ret) return ret;
462
463 if (index >= (int)symtab.size()) return -1;
464
465 return getSymName(elfFile, symtab[index].st_name, name);
466 }
467
createMaps(const char * elfPath,ifstream & elfFile,vector<unique_fd> & mapFds,const char * prefix,size_t sizeOfBpfMapDef)468 static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& mapFds,
469 const char* prefix, size_t sizeOfBpfMapDef) {
470 int ret;
471 vector<char> mdData;
472 vector<struct bpf_map_def> md;
473 vector<string> mapNames;
474 string fname = pathToFilename(string(elfPath), true);
475
476 ret = readSectionByName("maps", elfFile, mdData);
477 if (ret == -2) return 0; // no maps to read
478 if (ret) return ret;
479
480 if (mdData.size() % sizeOfBpfMapDef) {
481 ALOGE("createMaps failed due to improper sized maps section, %zu %% %zu != 0\n",
482 mdData.size(), sizeOfBpfMapDef);
483 return -1;
484 };
485
486 int mapCount = mdData.size() / sizeOfBpfMapDef;
487 md.resize(mapCount);
488 size_t trimmedSize = std::min(sizeOfBpfMapDef, sizeof(struct bpf_map_def));
489
490 const char* dataPtr = mdData.data();
491 for (auto& m : md) {
492 // First we zero initialize
493 memset(&m, 0, sizeof(m));
494 // Then we set non-zero defaults
495 m.bpfloader_max_ver = DEFAULT_BPFLOADER_MAX_VER; // v1.0
496 m.max_kver = 0xFFFFFFFFu; // matches KVER_INF from bpf_helpers.h
497 // Then we copy over the structure prefix from the ELF file.
498 memcpy(&m, dataPtr, trimmedSize);
499 // Move to next struct in the ELF file
500 dataPtr += sizeOfBpfMapDef;
501 }
502
503 ret = getSectionSymNames(elfFile, "maps", mapNames);
504 if (ret) return ret;
505
506 unsigned kvers = kernelVersion();
507
508 for (int i = 0; i < (int)mapNames.size(); i++) {
509 if (BPFLOADER_VERSION < md[i].bpfloader_min_ver) {
510 ALOGI("skipping map %s which requires bpfloader min ver 0x%05x\n", mapNames[i].c_str(),
511 md[i].bpfloader_min_ver);
512 mapFds.push_back(unique_fd());
513 continue;
514 }
515
516 if (BPFLOADER_VERSION >= md[i].bpfloader_max_ver) {
517 ALOGI("skipping map %s which requires bpfloader max ver 0x%05x\n", mapNames[i].c_str(),
518 md[i].bpfloader_max_ver);
519 mapFds.push_back(unique_fd());
520 continue;
521 }
522
523 if (kvers < md[i].min_kver) {
524 ALOGI("skipping map %s which requires kernel version 0x%x >= 0x%x\n",
525 mapNames[i].c_str(), kvers, md[i].min_kver);
526 mapFds.push_back(unique_fd());
527 continue;
528 }
529
530 if (kvers >= md[i].max_kver) {
531 ALOGI("skipping map %s which requires kernel version 0x%x < 0x%x\n",
532 mapNames[i].c_str(), kvers, md[i].max_kver);
533 mapFds.push_back(unique_fd());
534 continue;
535 }
536
537 // Format of pin location is /sys/fs/bpf/<prefix>map_<filename>_<mapname>
538 string mapPinLoc =
539 string(BPF_FS_PATH) + prefix + "map_" + fname + "_" + string(mapNames[i]);
540 bool reuse = false;
541 unique_fd fd;
542 int saved_errno;
543
544 if (access(mapPinLoc.c_str(), F_OK) == 0) {
545 fd.reset(bpf_obj_get(mapPinLoc.c_str()));
546 saved_errno = errno;
547 ALOGD("bpf_create_map reusing map %s, ret: %d\n", mapNames[i].c_str(), fd.get());
548 reuse = true;
549 } else {
550 enum bpf_map_type type = md[i].type;
551 if (type == BPF_MAP_TYPE_DEVMAP && !isAtLeastKernelVersion(4, 14, 0)) {
552 // On Linux Kernels older than 4.14 this map type doesn't exist, but it can kind
553 // of be approximated: ARRAY has the same userspace api, though it is not usable
554 // by the same ebpf programs. However, that's okay because the bpf_redirect_map()
555 // helper doesn't exist on 4.9 anyway (so the bpf program would fail to load,
556 // and thus needs to be tagged as 4.14+ either way), so there's nothing useful you
557 // could do with a DEVMAP anyway (that isn't already provided by an ARRAY)...
558 // Hence using an ARRAY instead of a DEVMAP simply makes life easier for userspace.
559 type = BPF_MAP_TYPE_ARRAY;
560 }
561 if (type == BPF_MAP_TYPE_DEVMAP_HASH && !isAtLeastKernelVersion(5, 4, 0)) {
562 // On Linux Kernels older than 5.4 this map type doesn't exist, but it can kind
563 // of be approximated: HASH has the same userspace visible api.
564 // However it cannot be used by ebpf programs in the same way.
565 // Since bpf_redirect_map() only requires 4.14, a program using a DEVMAP_HASH map
566 // would fail to load (due to trying to redirect to a HASH instead of DEVMAP_HASH).
567 // One must thus tag any BPF_MAP_TYPE_DEVMAP_HASH + bpf_redirect_map() using
568 // programs as being 5.4+...
569 type = BPF_MAP_TYPE_HASH;
570 }
571 fd.reset(bpf_create_map(type, mapNames[i].c_str(), md[i].key_size, md[i].value_size,
572 md[i].max_entries, md[i].map_flags));
573 saved_errno = errno;
574 ALOGD("bpf_create_map name %s, ret: %d\n", mapNames[i].c_str(), fd.get());
575 }
576
577 if (fd < 0) return -saved_errno;
578
579 if (!reuse) {
580 ret = bpf_obj_pin(fd, mapPinLoc.c_str());
581 if (ret) return -errno;
582 ret = chown(mapPinLoc.c_str(), (uid_t)md[i].uid, (gid_t)md[i].gid);
583 if (ret) return -errno;
584 ret = chmod(mapPinLoc.c_str(), md[i].mode);
585 if (ret) return -errno;
586 }
587
588 mapFds.push_back(std::move(fd));
589 }
590
591 return ret;
592 }
593
594 /* For debugging, dump all instructions */
dumpIns(char * ins,int size)595 static void dumpIns(char* ins, int size) {
596 for (int row = 0; row < size / 8; row++) {
597 ALOGE("%d: ", row);
598 for (int j = 0; j < 8; j++) {
599 ALOGE("%3x ", ins[(row * 8) + j]);
600 }
601 ALOGE("\n");
602 }
603 }
604
605 /* For debugging, dump all code sections from cs list */
dumpAllCs(vector<codeSection> & cs)606 static void dumpAllCs(vector<codeSection>& cs) {
607 for (int i = 0; i < (int)cs.size(); i++) {
608 ALOGE("Dumping cs %d, name %s\n", int(i), cs[i].name.c_str());
609 dumpIns((char*)cs[i].data.data(), cs[i].data.size());
610 ALOGE("-----------\n");
611 }
612 }
613
applyRelo(void * insnsPtr,Elf64_Addr offset,int fd)614 static void applyRelo(void* insnsPtr, Elf64_Addr offset, int fd) {
615 int insnIndex;
616 struct bpf_insn *insn, *insns;
617
618 insns = (struct bpf_insn*)(insnsPtr);
619
620 insnIndex = offset / sizeof(struct bpf_insn);
621 insn = &insns[insnIndex];
622
623 ALOGD(
624 "applying relo to instruction at byte offset: %d, \
625 insn offset %d , insn %lx\n",
626 (int)offset, (int)insnIndex, *(unsigned long*)insn);
627
628 if (insn->code != (BPF_LD | BPF_IMM | BPF_DW)) {
629 ALOGE("Dumping all instructions till ins %d\n", insnIndex);
630 ALOGE("invalid relo for insn %d: code 0x%x\n", insnIndex, insn->code);
631 dumpIns((char*)insnsPtr, (insnIndex + 3) * 8);
632 return;
633 }
634
635 insn->imm = fd;
636 insn->src_reg = BPF_PSEUDO_MAP_FD;
637 }
638
applyMapRelo(ifstream & elfFile,vector<unique_fd> & mapFds,vector<codeSection> & cs)639 static void applyMapRelo(ifstream& elfFile, vector<unique_fd> &mapFds, vector<codeSection>& cs) {
640 vector<string> mapNames;
641
642 int ret = getSectionSymNames(elfFile, "maps", mapNames);
643 if (ret) return;
644
645 for (int k = 0; k != (int)cs.size(); k++) {
646 Elf64_Rel* rel = (Elf64_Rel*)(cs[k].rel_data.data());
647 int n_rel = cs[k].rel_data.size() / sizeof(*rel);
648
649 for (int i = 0; i < n_rel; i++) {
650 int symIndex = ELF64_R_SYM(rel[i].r_info);
651 string symName;
652
653 ret = getSymNameByIdx(elfFile, symIndex, symName);
654 if (ret) return;
655
656 /* Find the map fd and apply relo */
657 for (int j = 0; j < (int)mapNames.size(); j++) {
658 if (!mapNames[j].compare(symName)) {
659 applyRelo(cs[k].data.data(), rel[i].r_offset, mapFds[j]);
660 break;
661 }
662 }
663 }
664 }
665 }
666
loadCodeSections(const char * elfPath,vector<codeSection> & cs,const string & license,const char * prefix)667 static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const string& license,
668 const char* prefix) {
669 unsigned kvers = kernelVersion();
670 int ret, fd;
671
672 if (!kvers) return -1;
673
674 string fname = pathToFilename(string(elfPath), true);
675
676 for (int i = 0; i < (int)cs.size(); i++) {
677 string name = cs[i].name;
678 unsigned bpfMinVer = DEFAULT_BPFLOADER_MIN_VER; // v0.0
679 unsigned bpfMaxVer = DEFAULT_BPFLOADER_MAX_VER; // v1.0
680
681 if (cs[i].prog_def.has_value()) {
682 unsigned min_kver = cs[i].prog_def->min_kver;
683 unsigned max_kver = cs[i].prog_def->max_kver;
684 ALOGD("cs[%d].name:%s min_kver:%x .max_kver:%x (kvers:%x)\n", i, name.c_str(), min_kver,
685 max_kver, kvers);
686 if (kvers < min_kver) continue;
687 if (kvers >= max_kver) continue;
688
689 bpfMinVer = cs[i].prog_def->bpfloader_min_ver;
690 bpfMaxVer = cs[i].prog_def->bpfloader_max_ver;
691 }
692
693 ALOGD("cs[%d].name:%s requires bpfloader version [0x%05x,0x%05x)\n", i, name.c_str(),
694 bpfMinVer, bpfMaxVer);
695 if (BPFLOADER_VERSION < bpfMinVer) continue;
696 if (BPFLOADER_VERSION >= bpfMaxVer) continue;
697
698 // strip any potential $foo suffix
699 // this can be used to provide duplicate programs
700 // conditionally loaded based on running kernel version
701 name = name.substr(0, name.find_last_of('$'));
702
703 bool reuse = false;
704 // Format of pin location is
705 // /sys/fs/bpf/<prefix>prog_<filename>_<mapname>
706 string progPinLoc = BPF_FS_PATH;
707 progPinLoc += prefix;
708 progPinLoc += "prog_";
709 progPinLoc += fname;
710 progPinLoc += '_';
711 progPinLoc += name;
712 if (access(progPinLoc.c_str(), F_OK) == 0) {
713 fd = retrieveProgram(progPinLoc.c_str());
714 ALOGD("New bpf prog load reusing prog %s, ret: %d (%s)\n", progPinLoc.c_str(), fd,
715 (fd < 0 ? std::strerror(errno) : "no error"));
716 reuse = true;
717 } else {
718 vector<char> log_buf(BPF_LOAD_LOG_SZ, 0);
719
720 fd = bpf_prog_load(cs[i].type, name.c_str(), (struct bpf_insn*)cs[i].data.data(),
721 cs[i].data.size(), license.c_str(), kvers, 0, log_buf.data(),
722 log_buf.size());
723 ALOGD("bpf_prog_load lib call for %s (%s) returned fd: %d (%s)\n", elfPath,
724 cs[i].name.c_str(), fd, (fd < 0 ? std::strerror(errno) : "no error"));
725
726 if (fd < 0) {
727 vector<string> lines = android::base::Split(log_buf.data(), "\n");
728
729 ALOGW("bpf_prog_load - BEGIN log_buf contents:");
730 for (const auto& line : lines) ALOGW("%s", line.c_str());
731 ALOGW("bpf_prog_load - END log_buf contents.");
732
733 if (cs[i].prog_def->optional) {
734 ALOGW("failed program is marked optional - continuing...");
735 continue;
736 }
737 ALOGE("non-optional program failed to load.");
738 }
739 }
740
741 if (fd < 0) return fd;
742 if (fd == 0) return -EINVAL;
743
744 if (!reuse) {
745 ret = bpf_obj_pin(fd, progPinLoc.c_str());
746 if (ret) return -errno;
747 if (cs[i].prog_def.has_value()) {
748 if (chown(progPinLoc.c_str(), (uid_t)cs[i].prog_def->uid,
749 (gid_t)cs[i].prog_def->gid)) {
750 return -errno;
751 }
752 }
753 if (chmod(progPinLoc.c_str(), 0440)) return -errno;
754 }
755
756 cs[i].prog_fd.reset(fd);
757 }
758
759 return 0;
760 }
761
loadProg(const char * elfPath,bool * isCritical,const char * prefix)762 int loadProg(const char* elfPath, bool* isCritical, const char* prefix) {
763 vector<char> license;
764 vector<char> critical;
765 vector<codeSection> cs;
766 vector<unique_fd> mapFds;
767 int ret;
768
769 if (!isCritical) return -1;
770 *isCritical = false;
771
772 ifstream elfFile(elfPath, ios::in | ios::binary);
773 if (!elfFile.is_open()) return -1;
774
775 ret = readSectionByName("critical", elfFile, critical);
776 *isCritical = !ret;
777
778 ret = readSectionByName("license", elfFile, license);
779 if (ret) {
780 ALOGE("Couldn't find license in %s\n", elfPath);
781 return ret;
782 } else {
783 ALOGD("Loading %s%s ELF object %s with license %s\n",
784 *isCritical ? "critical for " : "optional", *isCritical ? (char*)critical.data() : "",
785 elfPath, (char*)license.data());
786 }
787
788 // the following default values are for bpfloader V0.0 format which does not include them
789 unsigned int bpfLoaderMinVer =
790 readSectionUint("bpfloader_min_ver", elfFile, DEFAULT_BPFLOADER_MIN_VER);
791 unsigned int bpfLoaderMaxVer =
792 readSectionUint("bpfloader_max_ver", elfFile, DEFAULT_BPFLOADER_MAX_VER);
793 size_t sizeOfBpfMapDef =
794 readSectionUint("size_of_bpf_map_def", elfFile, DEFAULT_SIZEOF_BPF_MAP_DEF);
795 size_t sizeOfBpfProgDef =
796 readSectionUint("size_of_bpf_prog_def", elfFile, DEFAULT_SIZEOF_BPF_PROG_DEF);
797
798 // inclusive lower bound check
799 if (BPFLOADER_VERSION < bpfLoaderMinVer) {
800 ALOGI("BpfLoader version 0x%05x ignoring ELF object %s with min ver 0x%05x\n",
801 BPFLOADER_VERSION, elfPath, bpfLoaderMinVer);
802 return 0;
803 }
804
805 // exclusive upper bound check
806 if (BPFLOADER_VERSION >= bpfLoaderMaxVer) {
807 ALOGI("BpfLoader version 0x%05x ignoring ELF object %s with max ver 0x%05x\n",
808 BPFLOADER_VERSION, elfPath, bpfLoaderMaxVer);
809 return 0;
810 }
811
812 ALOGI("BpfLoader version 0x%05x processing ELF object %s with ver [0x%05x,0x%05x)\n",
813 BPFLOADER_VERSION, elfPath, bpfLoaderMinVer, bpfLoaderMaxVer);
814
815 if (sizeOfBpfMapDef < DEFAULT_SIZEOF_BPF_MAP_DEF) {
816 ALOGE("sizeof(bpf_map_def) of %zu is too small (< %d)\n", sizeOfBpfMapDef,
817 DEFAULT_SIZEOF_BPF_MAP_DEF);
818 return -1;
819 }
820
821 if (sizeOfBpfProgDef < DEFAULT_SIZEOF_BPF_PROG_DEF) {
822 ALOGE("sizeof(bpf_prog_def) of %zu is too small (< %d)\n", sizeOfBpfProgDef,
823 DEFAULT_SIZEOF_BPF_PROG_DEF);
824 return -1;
825 }
826
827 ret = readCodeSections(elfFile, cs, sizeOfBpfProgDef);
828 if (ret) {
829 ALOGE("Couldn't read all code sections in %s\n", elfPath);
830 return ret;
831 }
832
833 /* Just for future debugging */
834 if (0) dumpAllCs(cs);
835
836 ret = createMaps(elfPath, elfFile, mapFds, prefix, sizeOfBpfMapDef);
837 if (ret) {
838 ALOGE("Failed to create maps: (ret=%d) in %s\n", ret, elfPath);
839 return ret;
840 }
841
842 for (int i = 0; i < (int)mapFds.size(); i++)
843 ALOGD("map_fd found at %d is %d in %s\n", i, mapFds[i].get(), elfPath);
844
845 applyMapRelo(elfFile, mapFds, cs);
846
847 ret = loadCodeSections(elfPath, cs, string(license.data()), prefix);
848 if (ret) ALOGE("Failed to load programs, loadCodeSections ret=%d\n", ret);
849
850 return ret;
851 }
852
853 } // namespace bpf
854 } // namespace android
855