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 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 229 static bool symCompare(Elf64_Sym a, Elf64_Sym b) { 230 return (a.st_value < b.st_value); 231 } 232 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 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 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 */ 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 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 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 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 */ 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 */ 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 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 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 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 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