• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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