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