blob: 3e8a1f77099ec210a6168b44f4684b9894b4601a [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>
27#include <sys/utsname.h>
28#include <unistd.h>
29
30#include "LoaderUtils.h"
31#include "include/libbpf_android.h"
32
33#include <cstdlib>
34#include <fstream>
35#include <iostream>
36#include <string>
Christopher Ferrisc151c672019-02-01 15:31:26 -080037#include <vector>
Joel Fernandesd76a2002018-10-16 13:19:58 -070038
39#include <android-base/strings.h>
Connor O'Brien8d49fc72019-10-24 18:23:49 -070040#include <android-base/unique_fd.h>
Joel Fernandesd76a2002018-10-16 13:19:58 -070041
42#define BPF_FS_PATH "/sys/fs/bpf/"
43
44// Size of the BPF log buffer for verifier logging
45#define BPF_LOAD_LOG_SZ 0x1ffff
46
47using android::base::StartsWith;
Connor O'Brien8d49fc72019-10-24 18:23:49 -070048using android::base::unique_fd;
Joel Fernandesd76a2002018-10-16 13:19:58 -070049using std::ifstream;
50using std::ios;
Christopher Ferrisc151c672019-02-01 15:31:26 -080051using std::string;
Joel Fernandesd76a2002018-10-16 13:19:58 -070052using std::vector;
53
54namespace android {
55namespace bpf {
56
57typedef struct {
58 const char* name;
59 enum bpf_prog_type type;
60} sectionType;
61
62/*
63 * Map section name prefixes to program types, the section name will be:
64 * SEC(<prefix>/<name-of-program>)
65 * For example:
66 * SEC("tracepoint/sched_switch_func") where sched_switch_funcs
67 * is the name of the program, and tracepoint is the type.
68 */
69sectionType sectionNameTypes[] = {
Chenbo Feng5aee2f12018-12-26 16:14:05 -080070 {"kprobe", BPF_PROG_TYPE_KPROBE},
71 {"tracepoint", BPF_PROG_TYPE_TRACEPOINT},
72 {"skfilter", BPF_PROG_TYPE_SOCKET_FILTER},
73 {"cgroupskb", BPF_PROG_TYPE_CGROUP_SKB},
74 {"schedcls", BPF_PROG_TYPE_SCHED_CLS},
75 {"cgroupsock", BPF_PROG_TYPE_CGROUP_SOCK},
Joel Fernandesd76a2002018-10-16 13:19:58 -070076
77 /* End of table */
Chenbo Feng5aee2f12018-12-26 16:14:05 -080078 {"END", BPF_PROG_TYPE_UNSPEC},
Joel Fernandesd76a2002018-10-16 13:19:58 -070079};
80
81typedef struct {
82 enum bpf_prog_type type;
83 string name;
84 vector<char> data;
85 vector<char> rel_data;
86
Connor O'Brien8d49fc72019-10-24 18:23:49 -070087 unique_fd prog_fd; /* fd after loading */
Joel Fernandesd76a2002018-10-16 13:19:58 -070088} codeSection;
89
90/* Common with the eBPF C program */
91struct bpf_map_def {
92 enum bpf_map_type type;
93 unsigned int key_size;
94 unsigned int value_size;
95 unsigned int max_entries;
96 unsigned int map_flags;
97 unsigned int inner_map_idx;
98 unsigned int numa_node;
99};
100
101static int readElfHeader(ifstream& elfFile, Elf64_Ehdr* eh) {
102 elfFile.seekg(0);
103 if (elfFile.fail()) return -1;
104
105 if (!elfFile.read((char*)eh, sizeof(*eh))) return -1;
106
107 return 0;
108}
109
110/* Reads all section header tables into an Shdr array */
111static int readSectionHeadersAll(ifstream& elfFile, vector<Elf64_Shdr>& shTable) {
112 Elf64_Ehdr eh;
113 int ret = 0;
114
115 ret = readElfHeader(elfFile, &eh);
116 if (ret) return ret;
117
118 elfFile.seekg(eh.e_shoff);
119 if (elfFile.fail()) return -1;
120
121 /* Read shdr table entries */
122 shTable.resize(eh.e_shnum);
123
124 if (!elfFile.read((char*)shTable.data(), (eh.e_shnum * eh.e_shentsize))) return -ENOMEM;
125
126 return 0;
127}
128
129/* Read a section by its index - for ex to get sec hdr strtab blob */
130static int readSectionByIdx(ifstream& elfFile, int id, vector<char>& sec) {
131 vector<Elf64_Shdr> shTable;
132 int entries, ret = 0;
133
134 ret = readSectionHeadersAll(elfFile, shTable);
135 if (ret) return ret;
136 entries = shTable.size();
137
138 elfFile.seekg(shTable[id].sh_offset);
139 if (elfFile.fail()) return -1;
140
141 sec.resize(shTable[id].sh_size);
142 if (!elfFile.read(sec.data(), shTable[id].sh_size)) return -1;
143
144 return 0;
145}
146
147/* Read whole section header string table */
148static int readSectionHeaderStrtab(ifstream& elfFile, vector<char>& strtab) {
149 Elf64_Ehdr eh;
150 int ret = 0;
151
152 ret = readElfHeader(elfFile, &eh);
153 if (ret) return ret;
154
155 ret = readSectionByIdx(elfFile, eh.e_shstrndx, strtab);
156 if (ret) return ret;
157
158 return 0;
159}
160
161/* Get name from offset in strtab */
162static int getSymName(ifstream& elfFile, int nameOff, string& name) {
163 int ret;
164 vector<char> secStrTab;
165
166 ret = readSectionHeaderStrtab(elfFile, secStrTab);
167 if (ret) return ret;
168
169 if (nameOff >= (int)secStrTab.size()) return -1;
170
171 name = string((char*)secStrTab.data() + nameOff);
172 return 0;
173}
174
175/* Reads a full section by name - example to get the GPL license */
176static int readSectionByName(const char* name, ifstream& elfFile, vector<char>& data) {
177 vector<char> secStrTab;
178 vector<Elf64_Shdr> shTable;
179 int ret;
180
181 ret = readSectionHeadersAll(elfFile, shTable);
182 if (ret) return ret;
183
184 ret = readSectionHeaderStrtab(elfFile, secStrTab);
185 if (ret) return ret;
186
187 for (int i = 0; i < (int)shTable.size(); i++) {
188 char* secname = secStrTab.data() + shTable[i].sh_name;
189 if (!secname) continue;
190
191 if (!strcmp(secname, name)) {
192 vector<char> dataTmp;
193 dataTmp.resize(shTable[i].sh_size);
194
195 elfFile.seekg(shTable[i].sh_offset);
196 if (elfFile.fail()) return -1;
197
198 if (!elfFile.read((char*)dataTmp.data(), shTable[i].sh_size)) return -1;
199
200 data = dataTmp;
201 return 0;
202 }
203 }
204 return -2;
205}
206
207static int readSectionByType(ifstream& elfFile, int type, vector<char>& data) {
208 int ret;
209 vector<Elf64_Shdr> shTable;
210
211 ret = readSectionHeadersAll(elfFile, shTable);
212 if (ret) return ret;
213
214 for (int i = 0; i < (int)shTable.size(); i++) {
215 if ((int)shTable[i].sh_type != type) continue;
216
217 vector<char> dataTmp;
218 dataTmp.resize(shTable[i].sh_size);
219
220 elfFile.seekg(shTable[i].sh_offset);
221 if (elfFile.fail()) return -1;
222
223 if (!elfFile.read((char*)dataTmp.data(), shTable[i].sh_size)) return -1;
224
225 data = dataTmp;
226 return 0;
227 }
228 return -2;
229}
230
231static bool symCompare(Elf64_Sym a, Elf64_Sym b) {
232 return (a.st_value < b.st_value);
233}
234
235static int readSymTab(ifstream& elfFile, int sort, vector<Elf64_Sym>& data) {
236 int ret, numElems;
237 Elf64_Sym* buf;
238 vector<char> secData;
239
240 ret = readSectionByType(elfFile, SHT_SYMTAB, secData);
241 if (ret) return ret;
242
243 buf = (Elf64_Sym*)secData.data();
244 numElems = (secData.size() / sizeof(Elf64_Sym));
245 data.assign(buf, buf + numElems);
246
247 if (sort) std::sort(data.begin(), data.end(), symCompare);
248 return 0;
249}
250
251static enum bpf_prog_type getSectionType(string& name) {
252 for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++)
253 if (StartsWith(name, sectionNameTypes[i].name)) return sectionNameTypes[i].type;
254
255 return BPF_PROG_TYPE_UNSPEC;
256}
257
258/* If ever needed
259static string getSectionName(enum bpf_prog_type type)
260{
261 for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++)
262 if (sectionNameTypes[i].type == type)
263 return std::string(sectionNameTypes[i].name);
264
265 return NULL;
266}
267*/
268
269static bool isRelSection(codeSection& cs, string& name) {
270 for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++) {
271 sectionType st = sectionNameTypes[i];
272
273 if (st.type != cs.type) continue;
274
275 if (StartsWith(name, std::string(".rel") + st.name + "/"))
276 return true;
277 else
278 return false;
279 }
280 return false;
281}
282
283/* Read a section by its index - for ex to get sec hdr strtab blob */
284static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs) {
285 vector<Elf64_Shdr> shTable;
286 int entries, ret = 0;
287
288 ret = readSectionHeadersAll(elfFile, shTable);
289 if (ret) return ret;
290 entries = shTable.size();
291
292 for (int i = 0; i < entries; i++) {
293 string name;
294 codeSection cs_temp;
295 cs_temp.type = BPF_PROG_TYPE_UNSPEC;
296
297 ret = getSymName(elfFile, shTable[i].sh_name, name);
298 if (ret) return ret;
299
300 enum bpf_prog_type ptype = getSectionType(name);
301 if (ptype != BPF_PROG_TYPE_UNSPEC) {
302 deslash(name);
303 cs_temp.type = ptype;
304 cs_temp.name = name;
305
306 ret = readSectionByIdx(elfFile, i, cs_temp.data);
307 if (ret) return ret;
308 ALOGD("Loaded code section %d (%s)\n", i, name.c_str());
309 }
310
311 /* Check for rel section */
312 if (cs_temp.data.size() > 0 && i < entries) {
313 ret = getSymName(elfFile, shTable[i + 1].sh_name, name);
314 if (ret) return ret;
315
316 if (isRelSection(cs_temp, name)) {
317 ret = readSectionByIdx(elfFile, i + 1, cs_temp.rel_data);
318 if (ret) return ret;
319 ALOGD("Loaded relo section %d (%s)\n", i, name.c_str());
320 }
321 }
322
323 if (cs_temp.data.size() > 0) {
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700324 cs.push_back(std::move(cs_temp));
Joel Fernandesd76a2002018-10-16 13:19:58 -0700325 ALOGD("Adding section %d to cs list\n", i);
326 }
327 }
328 return 0;
329}
330
331static int getSymNameByIdx(ifstream& elfFile, int index, string& name) {
332 vector<Elf64_Sym> symtab;
333 int ret = 0;
334
335 ret = readSymTab(elfFile, 0 /* !sort */, symtab);
336 if (ret) return ret;
337
338 if (index >= (int)symtab.size()) return -1;
339
340 return getSymName(elfFile, symtab[index].st_name, name);
341}
342
343static int getMapNames(ifstream& elfFile, vector<string>& names) {
344 int ret;
345 string mapName;
346 vector<Elf64_Sym> symtab;
347 vector<Elf64_Shdr> shTable;
348
349 ret = readSymTab(elfFile, 1 /* sort */, symtab);
350 if (ret) return ret;
351
352 /* Get index of maps section */
353 ret = readSectionHeadersAll(elfFile, shTable);
354 if (ret) return ret;
355
356 int maps_idx = -1;
357 for (int i = 0; i < (int)shTable.size(); i++) {
358 ret = getSymName(elfFile, shTable[i].sh_name, mapName);
359 if (ret) return ret;
360
361 if (!mapName.compare("maps")) {
362 maps_idx = i;
363 break;
364 }
365 }
366
367 /* No maps found */
368 if (maps_idx == -1) {
369 ALOGE("No maps could be found in elf object\n");
370 return -1;
371 }
372
373 for (int i = 0; i < (int)symtab.size(); i++) {
374 if (symtab[i].st_shndx == maps_idx) {
375 string s;
376 ret = getSymName(elfFile, symtab[i].st_name, s);
377 if (ret) return ret;
378 names.push_back(s);
379 }
380 }
381
382 return 0;
383}
384
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700385static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& mapFds) {
386 int ret;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700387 vector<char> mdData;
388 vector<struct bpf_map_def> md;
389 vector<string> mapNames;
390 string fname = pathToFilename(string(elfPath), true);
391
392 ret = readSectionByName("maps", elfFile, mdData);
393 if (ret) return ret;
394 md.resize(mdData.size() / sizeof(struct bpf_map_def));
395 memcpy(md.data(), mdData.data(), mdData.size());
396
397 ret = getMapNames(elfFile, mapNames);
398 if (ret) return ret;
399
Joel Fernandesd76a2002018-10-16 13:19:58 -0700400 for (int i = 0; i < (int)mapNames.size(); i++) {
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700401 unique_fd fd;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700402 // Format of pin location is /sys/fs/bpf/map_<filename>_<mapname>
403 string mapPinLoc;
404 bool reuse = false;
405
406 mapPinLoc = string(BPF_FS_PATH) + "map_" + fname + "_" + string(mapNames[i]);
407 if (access(mapPinLoc.c_str(), F_OK) == 0) {
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700408 fd.reset(bpf_obj_get(mapPinLoc.c_str()));
409 ALOGD("bpf_create_map reusing map %s, ret: %d\n", mapNames[i].c_str(), fd.get());
Joel Fernandesd76a2002018-10-16 13:19:58 -0700410 reuse = true;
411 } else {
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700412 fd.reset(bpf_create_map(md[i].type, mapNames[i].c_str(), md[i].key_size, md[i].value_size,
413 md[i].max_entries, md[i].map_flags));
414 ALOGD("bpf_create_map name %s, ret: %d\n", mapNames[i].c_str(), fd.get());
Joel Fernandesd76a2002018-10-16 13:19:58 -0700415 }
416
417 if (fd < 0) return fd;
418 if (fd == 0) return -EINVAL;
419
420 if (!reuse) {
421 ret = bpf_obj_pin(fd, mapPinLoc.c_str());
422 if (ret < 0) return ret;
423 }
424
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700425 mapFds.push_back(std::move(fd));
Joel Fernandesd76a2002018-10-16 13:19:58 -0700426 }
427
428 return ret;
429}
430
431/* For debugging, dump all instructions */
432static void dumpIns(char* ins, int size) {
433 for (int row = 0; row < size / 8; row++) {
434 ALOGE("%d: ", row);
435 for (int j = 0; j < 8; j++) {
436 ALOGE("%3x ", ins[(row * 8) + j]);
437 }
438 ALOGE("\n");
439 }
440}
441
442/* For debugging, dump all code sections from cs list */
443static void dumpAllCs(vector<codeSection>& cs) {
444 for (int i = 0; i < (int)cs.size(); i++) {
445 ALOGE("Dumping cs %d, name %s\n", int(i), cs[i].name.c_str());
446 dumpIns((char*)cs[i].data.data(), cs[i].data.size());
447 ALOGE("-----------\n");
448 }
449}
450
451static void applyRelo(void* insnsPtr, Elf64_Addr offset, int fd) {
452 int insnIndex;
453 struct bpf_insn *insn, *insns;
454
455 insns = (struct bpf_insn*)(insnsPtr);
456
457 insnIndex = offset / sizeof(struct bpf_insn);
458 insn = &insns[insnIndex];
459
460 ALOGD(
461 "applying relo to instruction at byte offset: %d, \
462 insn offset %d , insn %lx\n",
463 (int)offset, (int)insnIndex, *(unsigned long*)insn);
464
465 if (insn->code != (BPF_LD | BPF_IMM | BPF_DW)) {
466 ALOGE("Dumping all instructions till ins %d\n", insnIndex);
467 ALOGE("invalid relo for insn %d: code 0x%x\n", insnIndex, insn->code);
468 dumpIns((char*)insnsPtr, (insnIndex + 3) * 8);
469 return;
470 }
471
472 insn->imm = fd;
473 insn->src_reg = BPF_PSEUDO_MAP_FD;
474}
475
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700476static void applyMapRelo(ifstream& elfFile, vector<unique_fd> &mapFds, vector<codeSection>& cs) {
Joel Fernandesd76a2002018-10-16 13:19:58 -0700477 vector<string> mapNames;
478
479 int ret = getMapNames(elfFile, mapNames);
480 if (ret) return;
481
482 for (int k = 0; k != (int)cs.size(); k++) {
483 Elf64_Rel* rel = (Elf64_Rel*)(cs[k].rel_data.data());
484 int n_rel = cs[k].rel_data.size() / sizeof(*rel);
485
486 for (int i = 0; i < n_rel; i++) {
487 int symIndex = ELF64_R_SYM(rel[i].r_info);
488 string symName;
489
490 ret = getSymNameByIdx(elfFile, symIndex, symName);
491 if (ret) return;
492
493 /* Find the map fd and apply relo */
494 for (int j = 0; j < (int)mapNames.size(); j++) {
495 if (!mapNames[j].compare(symName)) {
496 applyRelo(cs[k].data.data(), rel[i].r_offset, mapFds[j]);
497 break;
498 }
499 }
500 }
501 }
502}
503
Christopher Ferrisc151c672019-02-01 15:31:26 -0800504static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const string& license) {
Joel Fernandesd76a2002018-10-16 13:19:58 -0700505 int ret, fd, kvers;
506
507 if ((kvers = getMachineKvers()) < 0) return -1;
508
509 string fname = pathToFilename(string(elfPath), true);
510
511 for (int i = 0; i < (int)cs.size(); i++) {
512 string progPinLoc;
513 bool reuse = false;
514
515 // Format of pin location is
516 // /sys/fs/bpf/prog_<filename>_<mapname>
517 progPinLoc = string(BPF_FS_PATH) + "prog_" + fname + "_" + cs[i].name;
518 if (access(progPinLoc.c_str(), F_OK) == 0) {
519 fd = bpf_obj_get(progPinLoc.c_str());
520 ALOGD("New bpf prog load reusing prog %s, ret: %d\n", cs[i].name.c_str(), fd);
521 reuse = true;
522 } else {
523 vector<char> log_buf(BPF_LOAD_LOG_SZ, 0);
524
525 fd = bpf_prog_load(cs[i].type, cs[i].name.c_str(), (struct bpf_insn*)cs[i].data.data(),
526 cs[i].data.size(), license.c_str(), kvers, 0,
527 log_buf.data(), log_buf.size());
Joel Fernandes (Google)88eef6a2019-11-08 16:28:14 -0500528 ALOGD("bpf_prog_load lib call for %s (%s) returned: %d (%s)\n", elfPath,
529 cs[i].name.c_str(), fd, std::strerror(errno));
Joel Fernandesd76a2002018-10-16 13:19:58 -0700530
531 if (fd <= 0)
532 ALOGE("bpf_prog_load: log_buf contents: %s\n", (char *)log_buf.data());
533 }
534
535 if (fd < 0) return fd;
536 if (fd == 0) return -EINVAL;
537
538 if (!reuse) {
539 ret = bpf_obj_pin(fd, progPinLoc.c_str());
540 if (ret < 0) return ret;
541 }
542
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700543 cs[i].prog_fd.reset(fd);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700544 }
545
546 return 0;
547}
548
549int loadProg(const char* elfPath) {
550 vector<char> license;
551 vector<codeSection> cs;
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700552 vector<unique_fd> mapFds;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700553 int ret;
554
555 ifstream elfFile(elfPath, ios::in | ios::binary);
556 if (!elfFile.is_open()) return -1;
557
558 ret = readSectionByName("license", elfFile, license);
559 if (ret) {
560 ALOGE("Couldn't find license in %s\n", elfPath);
561 return ret;
562 } else {
563 ALOGD("Loading ELF object %s with license %s\n", elfPath, (char*)license.data());
564 }
565
566 ret = readCodeSections(elfFile, cs);
567 if (ret) {
568 ALOGE("Couldn't read all code sections in %s\n", elfPath);
569 return ret;
570 }
571
572 /* Just for future debugging */
573 if (0) dumpAllCs(cs);
574
575 ret = createMaps(elfPath, elfFile, mapFds);
576 if (ret) {
577 ALOGE("Failed to create maps: (ret=%d) in %s\n", ret, elfPath);
578 return ret;
579 }
580
581 for (int i = 0; i < (int)mapFds.size(); i++)
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700582 ALOGD("map_fd found at %d is %d in %s\n", i, mapFds[i].get(), elfPath);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700583
584 applyMapRelo(elfFile, mapFds, cs);
585
586 ret = loadCodeSections(elfPath, cs, string(license.data()));
587 if (ret) ALOGE("Failed to load programs, loadCodeSections ret=%d\n", ret);
588
589 return ret;
590}
591
592} // namespace bpf
593} // namespace android