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