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