blob: aac971097808b76e2e9ed205a0fe6bd2074b8381 [file] [log] [blame]
Joel Fernandesd76a2002018-10-16 13:19:58 -07001/*
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>
Maciej Żenczykowski83f29772020-01-27 03:11:51 -080027#include <sys/stat.h>
Joel Fernandesd76a2002018-10-16 13:19:58 -070028#include <sys/utsname.h>
29#include <unistd.h>
30
Maciej Żenczykowski730a3862020-01-27 01:10:48 -080031#include "../progs/include/bpf_map_def.h"
Maciej Żenczykowski07375e22020-02-19 14:23:59 -080032#include "bpf/BpfUtils.h"
Joel Fernandesd76a2002018-10-16 13:19:58 -070033#include "include/libbpf_android.h"
34
35#include <cstdlib>
36#include <fstream>
37#include <iostream>
Connor O'Brien3278a162020-02-13 21:45:22 -080038#include <optional>
Joel Fernandesd76a2002018-10-16 13:19:58 -070039#include <string>
Christopher Ferrisc151c672019-02-01 15:31:26 -080040#include <vector>
Joel Fernandesd76a2002018-10-16 13:19:58 -070041
Steven Moreland4891e612020-01-10 15:35:52 -080042#include <android-base/properties.h>
Joel Fernandesd76a2002018-10-16 13:19:58 -070043#include <android-base/strings.h>
Connor O'Brien8d49fc72019-10-24 18:23:49 -070044#include <android-base/unique_fd.h>
Joel Fernandesd76a2002018-10-16 13:19:58 -070045
46#define BPF_FS_PATH "/sys/fs/bpf/"
47
48// Size of the BPF log buffer for verifier logging
49#define BPF_LOAD_LOG_SZ 0x1ffff
50
51using android::base::StartsWith;
Connor O'Brien8d49fc72019-10-24 18:23:49 -070052using android::base::unique_fd;
Joel Fernandesd76a2002018-10-16 13:19:58 -070053using std::ifstream;
54using std::ios;
Connor O'Brien3278a162020-02-13 21:45:22 -080055using std::optional;
Christopher Ferrisc151c672019-02-01 15:31:26 -080056using std::string;
Joel Fernandesd76a2002018-10-16 13:19:58 -070057using std::vector;
58
59namespace android {
60namespace bpf {
61
Maciej Żenczykowskif7c0d992021-01-21 16:01:04 -080062static string pathToFilename(const string& path, bool noext = false) {
63 vector<string> spath = android::base::Split(path, "/");
64 string ret = spath.back();
65
66 if (noext) {
67 size_t lastindex = ret.find_last_of('.');
68 return ret.substr(0, lastindex);
69 }
70 return ret;
71}
72
Joel Fernandesd76a2002018-10-16 13:19:58 -070073typedef struct {
74 const char* name;
75 enum bpf_prog_type type;
76} sectionType;
77
78/*
79 * Map section name prefixes to program types, the section name will be:
80 * SEC(<prefix>/<name-of-program>)
81 * For example:
82 * SEC("tracepoint/sched_switch_func") where sched_switch_funcs
83 * is the name of the program, and tracepoint is the type.
84 */
85sectionType sectionNameTypes[] = {
Maciej Żenczykowskiaa462222021-01-07 14:24:45 -080086 {"kprobe", BPF_PROG_TYPE_KPROBE},
87 {"tracepoint", BPF_PROG_TYPE_TRACEPOINT},
88 {"skfilter", BPF_PROG_TYPE_SOCKET_FILTER},
89 {"cgroupskb", BPF_PROG_TYPE_CGROUP_SKB},
90 {"schedcls", BPF_PROG_TYPE_SCHED_CLS},
91 {"cgroupsock", BPF_PROG_TYPE_CGROUP_SOCK},
92 {"xdp", BPF_PROG_TYPE_XDP},
Joel Fernandesd76a2002018-10-16 13:19:58 -070093
Maciej Żenczykowskiaa462222021-01-07 14:24:45 -080094 /* End of table */
95 {"END", BPF_PROG_TYPE_UNSPEC},
Joel Fernandesd76a2002018-10-16 13:19:58 -070096};
97
98typedef struct {
99 enum bpf_prog_type type;
100 string name;
101 vector<char> data;
102 vector<char> rel_data;
Connor O'Brien3278a162020-02-13 21:45:22 -0800103 optional<struct bpf_prog_def> prog_def;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700104
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700105 unique_fd prog_fd; /* fd after loading */
Joel Fernandesd76a2002018-10-16 13:19:58 -0700106} codeSection;
107
Joel Fernandesd76a2002018-10-16 13:19:58 -0700108static int readElfHeader(ifstream& elfFile, Elf64_Ehdr* eh) {
109 elfFile.seekg(0);
110 if (elfFile.fail()) return -1;
111
112 if (!elfFile.read((char*)eh, sizeof(*eh))) return -1;
113
114 return 0;
115}
116
117/* Reads all section header tables into an Shdr array */
118static int readSectionHeadersAll(ifstream& elfFile, vector<Elf64_Shdr>& shTable) {
119 Elf64_Ehdr eh;
120 int ret = 0;
121
122 ret = readElfHeader(elfFile, &eh);
123 if (ret) return ret;
124
125 elfFile.seekg(eh.e_shoff);
126 if (elfFile.fail()) return -1;
127
128 /* Read shdr table entries */
129 shTable.resize(eh.e_shnum);
130
131 if (!elfFile.read((char*)shTable.data(), (eh.e_shnum * eh.e_shentsize))) return -ENOMEM;
132
133 return 0;
134}
135
136/* Read a section by its index - for ex to get sec hdr strtab blob */
137static int readSectionByIdx(ifstream& elfFile, int id, vector<char>& sec) {
138 vector<Elf64_Shdr> shTable;
Maciej Żenczykowskid56ec052021-01-15 00:27:04 -0800139 int ret = readSectionHeadersAll(elfFile, shTable);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700140 if (ret) return ret;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700141
142 elfFile.seekg(shTable[id].sh_offset);
143 if (elfFile.fail()) return -1;
144
145 sec.resize(shTable[id].sh_size);
146 if (!elfFile.read(sec.data(), shTable[id].sh_size)) return -1;
147
148 return 0;
149}
150
151/* Read whole section header string table */
152static int readSectionHeaderStrtab(ifstream& elfFile, vector<char>& strtab) {
153 Elf64_Ehdr eh;
Maciej Żenczykowskid56ec052021-01-15 00:27:04 -0800154 int ret = readElfHeader(elfFile, &eh);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700155 if (ret) return ret;
156
157 ret = readSectionByIdx(elfFile, eh.e_shstrndx, strtab);
158 if (ret) return ret;
159
160 return 0;
161}
162
163/* Get name from offset in strtab */
164static int getSymName(ifstream& elfFile, int nameOff, string& name) {
165 int ret;
166 vector<char> secStrTab;
167
168 ret = readSectionHeaderStrtab(elfFile, secStrTab);
169 if (ret) return ret;
170
171 if (nameOff >= (int)secStrTab.size()) return -1;
172
173 name = string((char*)secStrTab.data() + nameOff);
174 return 0;
175}
176
177/* Reads a full section by name - example to get the GPL license */
178static int readSectionByName(const char* name, ifstream& elfFile, vector<char>& data) {
179 vector<char> secStrTab;
180 vector<Elf64_Shdr> shTable;
181 int ret;
182
183 ret = readSectionHeadersAll(elfFile, shTable);
184 if (ret) return ret;
185
186 ret = readSectionHeaderStrtab(elfFile, secStrTab);
187 if (ret) return ret;
188
189 for (int i = 0; i < (int)shTable.size(); i++) {
190 char* secname = secStrTab.data() + shTable[i].sh_name;
191 if (!secname) continue;
192
193 if (!strcmp(secname, name)) {
194 vector<char> dataTmp;
195 dataTmp.resize(shTable[i].sh_size);
196
197 elfFile.seekg(shTable[i].sh_offset);
198 if (elfFile.fail()) return -1;
199
200 if (!elfFile.read((char*)dataTmp.data(), shTable[i].sh_size)) return -1;
201
202 data = dataTmp;
203 return 0;
204 }
205 }
206 return -2;
207}
208
209static int readSectionByType(ifstream& elfFile, int type, vector<char>& data) {
210 int ret;
211 vector<Elf64_Shdr> shTable;
212
213 ret = readSectionHeadersAll(elfFile, shTable);
214 if (ret) return ret;
215
216 for (int i = 0; i < (int)shTable.size(); i++) {
217 if ((int)shTable[i].sh_type != type) continue;
218
219 vector<char> dataTmp;
220 dataTmp.resize(shTable[i].sh_size);
221
222 elfFile.seekg(shTable[i].sh_offset);
223 if (elfFile.fail()) return -1;
224
225 if (!elfFile.read((char*)dataTmp.data(), shTable[i].sh_size)) return -1;
226
227 data = dataTmp;
228 return 0;
229 }
230 return -2;
231}
232
233static bool symCompare(Elf64_Sym a, Elf64_Sym b) {
234 return (a.st_value < b.st_value);
235}
236
237static int readSymTab(ifstream& elfFile, int sort, vector<Elf64_Sym>& data) {
238 int ret, numElems;
239 Elf64_Sym* buf;
240 vector<char> secData;
241
242 ret = readSectionByType(elfFile, SHT_SYMTAB, secData);
243 if (ret) return ret;
244
245 buf = (Elf64_Sym*)secData.data();
246 numElems = (secData.size() / sizeof(Elf64_Sym));
247 data.assign(buf, buf + numElems);
248
249 if (sort) std::sort(data.begin(), data.end(), symCompare);
250 return 0;
251}
252
253static enum bpf_prog_type getSectionType(string& name) {
254 for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++)
255 if (StartsWith(name, sectionNameTypes[i].name)) return sectionNameTypes[i].type;
256
257 return BPF_PROG_TYPE_UNSPEC;
258}
259
260/* If ever needed
261static string getSectionName(enum bpf_prog_type type)
262{
263 for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++)
264 if (sectionNameTypes[i].type == type)
Maciej Żenczykowskif7c0d992021-01-21 16:01:04 -0800265 return string(sectionNameTypes[i].name);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700266
267 return NULL;
268}
269*/
270
271static bool isRelSection(codeSection& cs, string& name) {
272 for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++) {
273 sectionType st = sectionNameTypes[i];
274
275 if (st.type != cs.type) continue;
276
Maciej Żenczykowskif7c0d992021-01-21 16:01:04 -0800277 if (StartsWith(name, string(".rel") + st.name + "/"))
Joel Fernandesd76a2002018-10-16 13:19:58 -0700278 return true;
279 else
280 return false;
281 }
282 return false;
283}
284
Connor O'Brien3278a162020-02-13 21:45:22 -0800285static int readProgDefs(ifstream& elfFile, vector<struct bpf_prog_def>& pd) {
286 vector<char> pdData;
287 int ret = readSectionByName("progs", elfFile, pdData);
288 if (ret == -2) return 0;
289 if (ret) return ret;
290
291 pd.resize(pdData.size() / sizeof(struct bpf_prog_def));
292 memcpy(pd.data(), pdData.data(), pdData.size());
293 return 0;
294}
295
296static int getSectionSymNames(ifstream& elfFile, const string& sectionName, vector<string>& names) {
297 int ret;
298 string name;
299 vector<Elf64_Sym> symtab;
300 vector<Elf64_Shdr> shTable;
301
302 ret = readSymTab(elfFile, 1 /* sort */, symtab);
303 if (ret) return ret;
304
305 /* Get index of section */
306 ret = readSectionHeadersAll(elfFile, shTable);
307 if (ret) return ret;
308
309 int sec_idx = -1;
310 for (int i = 0; i < (int)shTable.size(); i++) {
311 ret = getSymName(elfFile, shTable[i].sh_name, name);
312 if (ret) return ret;
313
314 if (!name.compare(sectionName)) {
315 sec_idx = i;
316 break;
317 }
318 }
319
320 /* No section found with matching name*/
321 if (sec_idx == -1) {
Maciej Żenczykowski21f34cb2020-07-20 18:44:33 -0700322 ALOGW("No %s section could be found in elf object\n", sectionName.c_str());
Connor O'Brien3278a162020-02-13 21:45:22 -0800323 return -1;
324 }
325
326 for (int i = 0; i < (int)symtab.size(); i++) {
327 if (symtab[i].st_shndx == sec_idx) {
328 string s;
329 ret = getSymName(elfFile, symtab[i].st_name, s);
330 if (ret) return ret;
331 names.push_back(s);
332 }
333 }
334
335 return 0;
336}
337
Joel Fernandesd76a2002018-10-16 13:19:58 -0700338/* Read a section by its index - for ex to get sec hdr strtab blob */
339static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs) {
340 vector<Elf64_Shdr> shTable;
341 int entries, ret = 0;
342
343 ret = readSectionHeadersAll(elfFile, shTable);
344 if (ret) return ret;
345 entries = shTable.size();
346
Connor O'Brien3278a162020-02-13 21:45:22 -0800347 vector<struct bpf_prog_def> pd;
348 ret = readProgDefs(elfFile, pd);
349 if (ret) return ret;
350 vector<string> progDefNames;
351 ret = getSectionSymNames(elfFile, "progs", progDefNames);
352 if (!pd.empty() && ret) return ret;
353
Joel Fernandesd76a2002018-10-16 13:19:58 -0700354 for (int i = 0; i < entries; i++) {
355 string name;
356 codeSection cs_temp;
357 cs_temp.type = BPF_PROG_TYPE_UNSPEC;
358
359 ret = getSymName(elfFile, shTable[i].sh_name, name);
360 if (ret) return ret;
361
362 enum bpf_prog_type ptype = getSectionType(name);
363 if (ptype != BPF_PROG_TYPE_UNSPEC) {
Connor O'Brien3278a162020-02-13 21:45:22 -0800364 string oldName = name;
Maciej Żenczykowskif7c0d992021-01-21 16:01:04 -0800365
366 // convert all slashes to underscores
367 std::replace(name.begin(), name.end(), '/', '_');
368
Joel Fernandesd76a2002018-10-16 13:19:58 -0700369 cs_temp.type = ptype;
370 cs_temp.name = name;
371
372 ret = readSectionByIdx(elfFile, i, cs_temp.data);
373 if (ret) return ret;
374 ALOGD("Loaded code section %d (%s)\n", i, name.c_str());
Connor O'Brien3278a162020-02-13 21:45:22 -0800375
376 vector<string> csSymNames;
377 ret = getSectionSymNames(elfFile, oldName, csSymNames);
378 if (ret || !csSymNames.size()) return ret;
379 for (size_t i = 0; i < progDefNames.size(); ++i) {
380 if (!progDefNames[i].compare(csSymNames[0] + "_def")) {
381 cs_temp.prog_def = pd[i];
382 break;
383 }
384 }
Joel Fernandesd76a2002018-10-16 13:19:58 -0700385 }
386
387 /* Check for rel section */
388 if (cs_temp.data.size() > 0 && i < entries) {
389 ret = getSymName(elfFile, shTable[i + 1].sh_name, name);
390 if (ret) return ret;
391
392 if (isRelSection(cs_temp, name)) {
393 ret = readSectionByIdx(elfFile, i + 1, cs_temp.rel_data);
394 if (ret) return ret;
395 ALOGD("Loaded relo section %d (%s)\n", i, name.c_str());
396 }
397 }
398
399 if (cs_temp.data.size() > 0) {
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700400 cs.push_back(std::move(cs_temp));
Joel Fernandesd76a2002018-10-16 13:19:58 -0700401 ALOGD("Adding section %d to cs list\n", i);
402 }
403 }
404 return 0;
405}
406
407static int getSymNameByIdx(ifstream& elfFile, int index, string& name) {
408 vector<Elf64_Sym> symtab;
409 int ret = 0;
410
411 ret = readSymTab(elfFile, 0 /* !sort */, symtab);
412 if (ret) return ret;
413
414 if (index >= (int)symtab.size()) return -1;
415
416 return getSymName(elfFile, symtab[index].st_name, name);
417}
418
Maciej Żenczykowskid8a45782021-01-14 23:36:32 -0800419static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& mapFds,
420 const char* prefix) {
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700421 int ret;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700422 vector<char> mdData;
423 vector<struct bpf_map_def> md;
424 vector<string> mapNames;
425 string fname = pathToFilename(string(elfPath), true);
426
427 ret = readSectionByName("maps", elfFile, mdData);
Steven Morelandc0905b42019-12-12 14:21:20 -0800428 if (ret == -2) return 0; // no maps to read
Joel Fernandesd76a2002018-10-16 13:19:58 -0700429 if (ret) return ret;
430 md.resize(mdData.size() / sizeof(struct bpf_map_def));
431 memcpy(md.data(), mdData.data(), mdData.size());
432
Connor O'Brien3278a162020-02-13 21:45:22 -0800433 ret = getSectionSymNames(elfFile, "maps", mapNames);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700434 if (ret) return ret;
435
Joel Fernandesd76a2002018-10-16 13:19:58 -0700436 for (int i = 0; i < (int)mapNames.size(); i++) {
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700437 unique_fd fd;
Maciej Żenczykowskid8a45782021-01-14 23:36:32 -0800438 // Format of pin location is /sys/fs/bpf/<prefix>map_<filename>_<mapname>
Joel Fernandesd76a2002018-10-16 13:19:58 -0700439 string mapPinLoc;
440 bool reuse = false;
441
Maciej Żenczykowskid8a45782021-01-14 23:36:32 -0800442 mapPinLoc = string(BPF_FS_PATH) + prefix + "map_" + fname + "_" + string(mapNames[i]);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700443 if (access(mapPinLoc.c_str(), F_OK) == 0) {
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700444 fd.reset(bpf_obj_get(mapPinLoc.c_str()));
445 ALOGD("bpf_create_map reusing map %s, ret: %d\n", mapNames[i].c_str(), fd.get());
Joel Fernandesd76a2002018-10-16 13:19:58 -0700446 reuse = true;
447 } else {
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700448 fd.reset(bpf_create_map(md[i].type, mapNames[i].c_str(), md[i].key_size, md[i].value_size,
449 md[i].max_entries, md[i].map_flags));
450 ALOGD("bpf_create_map name %s, ret: %d\n", mapNames[i].c_str(), fd.get());
Joel Fernandesd76a2002018-10-16 13:19:58 -0700451 }
452
453 if (fd < 0) return fd;
454 if (fd == 0) return -EINVAL;
455
456 if (!reuse) {
457 ret = bpf_obj_pin(fd, mapPinLoc.c_str());
Maciej Żenczykowski83f29772020-01-27 03:11:51 -0800458 if (ret) return -errno;
459 ret = chown(mapPinLoc.c_str(), (uid_t)md[i].uid, (gid_t)md[i].gid);
460 if (ret) return -errno;
461 ret = chmod(mapPinLoc.c_str(), md[i].mode);
462 if (ret) return -errno;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700463 }
464
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700465 mapFds.push_back(std::move(fd));
Joel Fernandesd76a2002018-10-16 13:19:58 -0700466 }
467
468 return ret;
469}
470
471/* For debugging, dump all instructions */
472static void dumpIns(char* ins, int size) {
473 for (int row = 0; row < size / 8; row++) {
474 ALOGE("%d: ", row);
475 for (int j = 0; j < 8; j++) {
476 ALOGE("%3x ", ins[(row * 8) + j]);
477 }
478 ALOGE("\n");
479 }
480}
481
482/* For debugging, dump all code sections from cs list */
483static void dumpAllCs(vector<codeSection>& cs) {
484 for (int i = 0; i < (int)cs.size(); i++) {
485 ALOGE("Dumping cs %d, name %s\n", int(i), cs[i].name.c_str());
486 dumpIns((char*)cs[i].data.data(), cs[i].data.size());
487 ALOGE("-----------\n");
488 }
489}
490
491static void applyRelo(void* insnsPtr, Elf64_Addr offset, int fd) {
492 int insnIndex;
493 struct bpf_insn *insn, *insns;
494
495 insns = (struct bpf_insn*)(insnsPtr);
496
497 insnIndex = offset / sizeof(struct bpf_insn);
498 insn = &insns[insnIndex];
499
500 ALOGD(
501 "applying relo to instruction at byte offset: %d, \
502 insn offset %d , insn %lx\n",
503 (int)offset, (int)insnIndex, *(unsigned long*)insn);
504
505 if (insn->code != (BPF_LD | BPF_IMM | BPF_DW)) {
506 ALOGE("Dumping all instructions till ins %d\n", insnIndex);
507 ALOGE("invalid relo for insn %d: code 0x%x\n", insnIndex, insn->code);
508 dumpIns((char*)insnsPtr, (insnIndex + 3) * 8);
509 return;
510 }
511
512 insn->imm = fd;
513 insn->src_reg = BPF_PSEUDO_MAP_FD;
514}
515
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700516static void applyMapRelo(ifstream& elfFile, vector<unique_fd> &mapFds, vector<codeSection>& cs) {
Joel Fernandesd76a2002018-10-16 13:19:58 -0700517 vector<string> mapNames;
518
Connor O'Brien3278a162020-02-13 21:45:22 -0800519 int ret = getSectionSymNames(elfFile, "maps", mapNames);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700520 if (ret) return;
521
522 for (int k = 0; k != (int)cs.size(); k++) {
523 Elf64_Rel* rel = (Elf64_Rel*)(cs[k].rel_data.data());
524 int n_rel = cs[k].rel_data.size() / sizeof(*rel);
525
526 for (int i = 0; i < n_rel; i++) {
527 int symIndex = ELF64_R_SYM(rel[i].r_info);
528 string symName;
529
530 ret = getSymNameByIdx(elfFile, symIndex, symName);
531 if (ret) return;
532
533 /* Find the map fd and apply relo */
534 for (int j = 0; j < (int)mapNames.size(); j++) {
535 if (!mapNames[j].compare(symName)) {
536 applyRelo(cs[k].data.data(), rel[i].r_offset, mapFds[j]);
537 break;
538 }
539 }
540 }
541 }
542}
543
Maciej Żenczykowskid8a45782021-01-14 23:36:32 -0800544static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const string& license,
545 const char* prefix) {
Maciej Żenczykowski07375e22020-02-19 14:23:59 -0800546 unsigned kvers = kernelVersion();
547 int ret, fd;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700548
Maciej Żenczykowski07375e22020-02-19 14:23:59 -0800549 if (!kvers) return -1;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700550
551 string fname = pathToFilename(string(elfPath), true);
552
553 for (int i = 0; i < (int)cs.size(); i++) {
Maciej Żenczykowski681f6042020-04-21 15:34:18 -0700554 string name = cs[i].name;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700555
Maciej Żenczykowski07375e22020-02-19 14:23:59 -0800556 if (cs[i].prog_def.has_value()) {
Maciej Żenczykowski681f6042020-04-21 15:34:18 -0700557 unsigned min_kver = cs[i].prog_def->min_kver;
558 unsigned max_kver = cs[i].prog_def->max_kver;
559 ALOGD("cs[%d].name:%s min_kver:%x .max_kver:%x (kvers:%x)\n", i, name.c_str(), min_kver,
560 max_kver, kvers);
561 if (kvers < min_kver) continue;
562 if (kvers >= max_kver) continue;
Maciej Żenczykowski07375e22020-02-19 14:23:59 -0800563 }
564
Maciej Żenczykowski681f6042020-04-21 15:34:18 -0700565 // strip any potential $foo suffix
566 // this can be used to provide duplicate programs
567 // conditionally loaded based on running kernel version
Maciej Żenczykowski428843d2020-04-23 12:43:44 -0700568 name = name.substr(0, name.find_last_of('$'));
Maciej Żenczykowski681f6042020-04-21 15:34:18 -0700569
570 bool reuse = false;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700571 // Format of pin location is
Maciej Żenczykowskid8a45782021-01-14 23:36:32 -0800572 // /sys/fs/bpf/<prefix>prog_<filename>_<mapname>
573 string progPinLoc = BPF_FS_PATH;
574 progPinLoc += prefix;
575 progPinLoc += "prog_";
Maciej Żenczykowski6c7871b2020-04-23 12:46:00 -0700576 progPinLoc += fname;
577 progPinLoc += '_';
578 progPinLoc += name;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700579 if (access(progPinLoc.c_str(), F_OK) == 0) {
Maciej Żenczykowskiaa295c82020-06-16 17:02:48 -0700580 fd = retrieveProgram(progPinLoc.c_str());
581 ALOGD("New bpf prog load reusing prog %s, ret: %d (%s)\n", progPinLoc.c_str(), fd,
582 (fd < 0 ? std::strerror(errno) : "no error"));
Joel Fernandesd76a2002018-10-16 13:19:58 -0700583 reuse = true;
584 } else {
585 vector<char> log_buf(BPF_LOAD_LOG_SZ, 0);
586
Maciej Żenczykowski681f6042020-04-21 15:34:18 -0700587 fd = bpf_prog_load(cs[i].type, name.c_str(), (struct bpf_insn*)cs[i].data.data(),
588 cs[i].data.size(), license.c_str(), kvers, 0, log_buf.data(),
589 log_buf.size());
Steven Moreland804bca02019-12-12 17:21:23 -0800590 ALOGD("bpf_prog_load lib call for %s (%s) returned fd: %d (%s)\n", elfPath,
591 cs[i].name.c_str(), fd, (fd < 0 ? std::strerror(errno) : "no error"));
Joel Fernandesd76a2002018-10-16 13:19:58 -0700592
Maciej Żenczykowski524deef2020-02-11 11:12:37 -0800593 if (fd < 0) {
Maciej Żenczykowskif7c0d992021-01-21 16:01:04 -0800594 vector<string> lines = android::base::Split(log_buf.data(), "\n");
Maciej Żenczykowski524deef2020-02-11 11:12:37 -0800595
Maciej Żenczykowskiaa295c82020-06-16 17:02:48 -0700596 ALOGW("bpf_prog_load - BEGIN log_buf contents:");
597 for (const auto& line : lines) ALOGW("%s", line.c_str());
598 ALOGW("bpf_prog_load - END log_buf contents.");
599
600 if (cs[i].prog_def->optional) {
601 ALOGW("failed program is marked optional - continuing...");
602 continue;
603 }
604 ALOGE("non-optional program failed to load.");
Maciej Żenczykowski524deef2020-02-11 11:12:37 -0800605 }
Joel Fernandesd76a2002018-10-16 13:19:58 -0700606 }
607
608 if (fd < 0) return fd;
609 if (fd == 0) return -EINVAL;
610
611 if (!reuse) {
612 ret = bpf_obj_pin(fd, progPinLoc.c_str());
Connor O'Brien3278a162020-02-13 21:45:22 -0800613 if (ret) return -errno;
614 if (cs[i].prog_def.has_value()) {
615 if (chown(progPinLoc.c_str(), (uid_t)cs[i].prog_def->uid,
616 (gid_t)cs[i].prog_def->gid)) {
617 return -errno;
618 }
619 }
620 if (chmod(progPinLoc.c_str(), 0440)) return -errno;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700621 }
622
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700623 cs[i].prog_fd.reset(fd);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700624 }
625
626 return 0;
627}
628
Maciej Żenczykowskid8a45782021-01-14 23:36:32 -0800629int loadProg(const char* elfPath, bool* isCritical, const char* prefix) {
Joel Fernandesd76a2002018-10-16 13:19:58 -0700630 vector<char> license;
Maciej Żenczykowski4ba8c1c2020-06-10 15:49:31 -0700631 vector<char> critical;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700632 vector<codeSection> cs;
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700633 vector<unique_fd> mapFds;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700634 int ret;
635
Maciej Żenczykowski89515d92020-06-14 19:27:33 -0700636 if (!isCritical) return -1;
637 *isCritical = false;
638
Joel Fernandesd76a2002018-10-16 13:19:58 -0700639 ifstream elfFile(elfPath, ios::in | ios::binary);
640 if (!elfFile.is_open()) return -1;
641
Maciej Żenczykowski4ba8c1c2020-06-10 15:49:31 -0700642 ret = readSectionByName("critical", elfFile, critical);
Maciej Żenczykowski89515d92020-06-14 19:27:33 -0700643 *isCritical = !ret;
Maciej Żenczykowski4ba8c1c2020-06-10 15:49:31 -0700644
Joel Fernandesd76a2002018-10-16 13:19:58 -0700645 ret = readSectionByName("license", elfFile, license);
646 if (ret) {
647 ALOGE("Couldn't find license in %s\n", elfPath);
648 return ret;
649 } else {
Maciej Żenczykowski4ba8c1c2020-06-10 15:49:31 -0700650 ALOGD("Loading %s%s ELF object %s with license %s\n",
Maciej Żenczykowski89515d92020-06-14 19:27:33 -0700651 *isCritical ? "critical for " : "optional", *isCritical ? (char*)critical.data() : "",
Maciej Żenczykowski4ba8c1c2020-06-10 15:49:31 -0700652 elfPath, (char*)license.data());
Joel Fernandesd76a2002018-10-16 13:19:58 -0700653 }
654
655 ret = readCodeSections(elfFile, cs);
656 if (ret) {
657 ALOGE("Couldn't read all code sections in %s\n", elfPath);
658 return ret;
659 }
660
661 /* Just for future debugging */
662 if (0) dumpAllCs(cs);
663
Maciej Żenczykowskid8a45782021-01-14 23:36:32 -0800664 ret = createMaps(elfPath, elfFile, mapFds, prefix);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700665 if (ret) {
666 ALOGE("Failed to create maps: (ret=%d) in %s\n", ret, elfPath);
667 return ret;
668 }
669
670 for (int i = 0; i < (int)mapFds.size(); i++)
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700671 ALOGD("map_fd found at %d is %d in %s\n", i, mapFds[i].get(), elfPath);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700672
673 applyMapRelo(elfFile, mapFds, cs);
674
Maciej Żenczykowskid8a45782021-01-14 23:36:32 -0800675 ret = loadCodeSections(elfPath, cs, string(license.data()), prefix);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700676 if (ret) ALOGE("Failed to load programs, loadCodeSections ret=%d\n", ret);
677
678 return ret;
679}
680
Maciej Żenczykowski2090e342020-06-16 17:56:16 -0700681static bool waitSecondsForProgsLoaded(int seconds) {
682 bool ok =
683 android::base::WaitForProperty("bpf.progs_loaded", "1", std::chrono::seconds(seconds));
684 if (!ok) ALOGW("Waited %ds for bpf.progs_loaded, still waiting...", seconds);
685 return ok;
686}
687
Steven Moreland4891e612020-01-10 15:35:52 -0800688void waitForProgsLoaded() {
Maciej Żenczykowski2090e342020-06-16 17:56:16 -0700689 if (waitSecondsForProgsLoaded(5)) return;
690 if (waitSecondsForProgsLoaded(10)) return;
691 if (waitSecondsForProgsLoaded(20)) return;
692 while (!waitSecondsForProgsLoaded(60))
693 ; // loop until success
Steven Moreland4891e612020-01-10 15:35:52 -0800694}
695
Joel Fernandesd76a2002018-10-16 13:19:58 -0700696} // namespace bpf
697} // namespace android