1// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 3#ifndef _GNU_SOURCE 4#define _GNU_SOURCE 5#endif 6 7#ifdef HAVE_LIBELF 8#include <libelf.h> 9#include <gelf.h> 10#endif 11 12#include <fcntl.h> 13#include <linux/kernel.h> 14 15#include "libbpf_internal.h" 16#include "str_error.h" 17 18 19 20#define STRERR_BUFSIZE 128 21 22/* A SHT_GNU_versym section holds 16-bit words. This bit is set if 23 * the symbol is hidden and can only be seen when referenced using an 24 * explicit version number. This is a GNU extension. 25 */ 26#define VERSYM_HIDDEN 0x8000 27 28/* This is the mask for the rest of the data in a word read from a 29 * SHT_GNU_versym section. 30 */ 31#define VERSYM_VERSION 0x7fff 32 33 34#ifdef HAVE_LIBELF 35int elf_open(const char *binary_path, struct elf_fd *elf_fd) 36{ 37 char errmsg[STRERR_BUFSIZE]; 38 int fd, ret; 39 Elf *elf; 40 41 if (elf_version(EV_CURRENT) == EV_NONE) { 42 pr_warn("elf: failed to init libelf for %s\n", binary_path); 43 return -LIBBPF_ERRNO__LIBELF; 44 } 45 fd = open(binary_path, O_RDONLY | O_CLOEXEC); 46 if (fd < 0) { 47 ret = -errno; 48 pr_warn("elf: failed to open %s: %s\n", binary_path, 49 libbpf_strerror_r(ret, errmsg, sizeof(errmsg))); 50 return ret; 51 } 52 elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); 53 if (!elf) { 54 pr_warn("elf: could not read elf from %s: %s\n", binary_path, elf_errmsg(-1)); 55 close(fd); 56 return -LIBBPF_ERRNO__FORMAT; 57 } 58 elf_fd->fd = fd; 59 elf_fd->elf = elf; 60 return 0; 61} 62#elif HAVE_ELFIO 63int elf_open(const char *binary_path, struct elf_fd *elf_fd) 64{ 65 pelfio_t pelfio = elfio_new(); 66 bool ret = false; 67 ret = elfio_load(pelfio, binary_path); 68 if (!ret) { 69 pr_warn("elf: could not read elf from %s: %s\n", binary_path, elf_errmsg(-1)); 70 return -LIBBPF_ERRNO__FORMAT; 71 } 72 elf_fd->elf = pelfio; 73 elf_fd->fd = -1; 74 return 0; 75} 76#endif 77 78 79void elf_close(struct elf_fd *elf_fd) 80{ 81 if (!elf_fd) 82 return; 83#ifdef HAVE_LIBELF 84 elf_end(elf_fd->elf); 85 close(elf_fd->fd); 86#elif HAVE_ELFIO 87 elfio_delete(elf_fd->elf); 88#endif 89} 90 91/* Return next ELF section of sh_type after scn, or first of that type if scn is NULL. */ 92#ifdef HAVE_LIBELF 93static Elf_Scn *elf_find_next_scn_by_type(Elf *elf, int sh_type, Elf_Scn *scn) 94{ 95 while ((scn = elf_nextscn(elf, scn)) != NULL) { 96 GElf_Shdr sh; 97 98 if (!gelf_getshdr(scn, &sh)) 99 continue; 100 if (sh.sh_type == sh_type) 101 return scn; 102 } 103 return NULL; 104} 105#elif HAVE_ELFIO 106static psection_t elf_find_next_scn_by_type(pelfio_t pelfio, int sh_type, psection_t pscn) 107{ 108 int secno = elfio_get_sections_num(pelfio); 109 int j = 0; 110 if (pscn != NULL) { 111 for (int i = 0; i < secno; i++) { 112 psection_t psection = elfio_get_section_by_index(pelfio, i); 113 if (psection == pscn) { 114 j = i; 115 } 116 } 117 } 118 for (; j < secno; j++) { 119 psection_t psection = elfio_get_section_by_index(pelfio, j); 120 Elf_Word sec_type = elfio_section_get_type(psection); 121 if (sec_type == sh_type) { 122 return psection; 123 } 124 } 125 return NULL; 126} 127#endif 128 129static int elf_sym_iter_new(struct elf_sym_iter *iter,pelfio_t elf, const char *binary_path, int sh_type, int st_type) 130{ 131 psection_t pSec = NULL; 132 memset(iter, 0, sizeof(*iter)); 133 pSec = elf_find_next_scn_by_type(elf, sh_type, NULL); 134 iter->strtabidx = elfio_section_get_link(pSec); 135 iter->syms->d_buf = (void*)elfio_section_get_data(pSec); 136 iter->syms->d_size = elfio_section_get_size(pSec); 137 if (!iter->syms->d_buf) { 138 pr_warn("elf: failed to get symbols for symtab section in '%s': %s\n", 139 binary_path, elf_errmsg(-1)); 140 return -EINVAL; 141 } 142 iter->nr_syms = iter->syms->d_size / elfio_section_get_entry_size(pSec); 143 iter->symsSec = pSec; 144 iter->elf = elf; 145 iter->st_type = st_type; 146 /* Version symbol table is meaningful to dynsym only */ 147 if (sh_type != SHT_DYNSYM) 148 return 0; 149 pSec = elf_find_next_scn_by_type(elf, SHT_GNU_versym, NULL); 150 if (!pSec) { 151 return 0; 152 } 153 iter->versyms->d_buf = (void*)elfio_section_get_data(pSec); 154 iter->versyms->d_size = elfio_section_get_size(pSec); 155 pSec = elf_find_next_scn_by_type(elf, SHT_GNU_verdef, NULL); 156 if (!pSec) { 157 return 0; 158 } 159 iter->verdefs->d_buf = (void*)elfio_section_get_data(pSec); 160 iter->verdefs->d_size = elfio_section_get_size(pSec); 161 if (!iter->verdefs->d_buf) { 162 pr_warn("elf: failed to get verdef ELF section in '%s'\n", binary_path); 163 return -EINVAL; 164 } 165 iter->verdef_strtabidx = elfio_section_get_link(pSec); 166 return 0; 167} 168 169#ifdef HAVA_ELFIO 170static GElf_Shdr *elf_sec_hdr_by_idx(const pelfio_t elf, size_t idx, GElf_Shdr *sheader) 171{ 172 psection_t psection = elfio_get_section_by_index(elf, idx); 173 sheader->sh_name = elfio_section_get_name_string_offset(psection); 174 sheader->sh_type = elfio_section_get_type(psection); 175 sheader->sh_flags = elfio_section_get_flags(psection); 176 sheader->sh_addr = elfio_section_get_address(psection); 177 sheader->sh_offset = elfio_section_get_offset(psection); 178 sheader->sh_size = elfio_section_get_size(psection); 179 sheader->sh_link = elfio_section_get_link(psection); 180 sheader->sh_info = elfio_section_get_info(psection); 181 sheader->sh_addralign = elfio_section_get_addr_align(psection); 182 sheader->sh_entsize = elfio_section_get_entry_size(psection); 183 return sheader; 184} 185#endif //HAVA_ELFIO 186 187 188static struct elf_sym *elf_sym_iter_next(struct elf_sym_iter *iter) 189{ 190 struct elf_sym *ret = &iter->sym; 191 GElf_Sym *sym = &ret->sym; 192 GElf_Versym versym; 193#ifdef HAVA_LIBELF 194 Elf_Scn *sym_scn; 195#elif HAVE_ELFIO 196 psection_t sym_scn; 197#endif 198 const char *name = NULL; 199 size_t idx; 200 for (idx = iter->next_sym_idx; idx < iter->nr_syms; idx++) { 201#ifdef HAVA_LIBELF 202 if (!gelf_getsym(iter->syms, idx, sym)) 203 continue; 204 if (GELF_ST_TYPE(sym->st_info) != iter->st_type) 205 continue; 206 name = elf_strptr(iter->elf, iter->strtabidx, sym->st_name); 207 if (!name) 208 continue; 209 sym_scn = elf_getscn(iter->elf, sym->st_shndx); 210 if (!sym_scn) 211 continue; 212 if (!gelf_getshdr(sym_scn, &ret->sh)) 213 continue; 214#elif HAVA_ELFIO 215 if(memcpy(sym,iter->sysms->d_buf + idx,sizeof(GElf_Sym) == NULL) { 216 continue; 217 } 218 if(((sym->st_info) & 0xf) != iter->st_type) { 219 continue; 220 } 221 psection_t psection = elfio_get_section_by_index(iter->elf, iter->strtabidx); 222 if (!psection) 223 return -LIBBPF_ERRNO__FORMAT; 224 pstring_t strstring = elfio_string_section_accessor_new(psection); 225 name = elfio_string_get_string(strstring, sym->st_name); 226 if(!name) { 227 continue; 228 } 229 if(!elf_sec_hdr_by_idx(iter->elf, sym->st_shndx, &ret->sh)) { 230 continue; 231 } 232#endif 233 iter->next_sym_idx = idx + 1; 234 ret->name = name; 235 ret->ver = 0; 236 ret->hidden = false; 237 if (iter->versyms) { 238#ifdef HAVA_LIBELF 239 if (!gelf_getversym(iter->versyms, idx, &versym)) 240 continue; 241#elif HAVA_ELFIO 242 versym = (GElf_Versym)iter->versysm->d_buf[idx]; 243#endif 244 ret->ver = versym & VERSYM_VERSION; 245 ret->hidden = versym & VERSYM_HIDDEN; 246 } 247 return ret; 248 } 249 return NULL; 250} 251 252static const char *elf_get_vername(struct elf_sym_iter *iter, int ver) 253{ 254 GElf_Verdaux verdaux; 255 GElf_Verdef verdef; 256 int offset; 257 if (!iter->verdefs) 258 return NULL; 259 offset = 0; 260#ifdef HAVE_LIBELF 261 while (gelf_getverdef(iter->verdefs, offset, &verdef)) { 262 if (verdef.vd_ndx != ver) { 263 if (!verdef.vd_next) 264 break; 265 offset += verdef.vd_next; 266 continue; 267 } 268 if (!gelf_getverdaux(iter->verdefs, offset + verdef.vd_aux, &verdaux)) 269 break; 270 return elf_strptr(iter->elf, iter->verdef_strtabidx, verdaux.vda_name); 271 } 272#elif HAVE_ELFIO 273 while (memcpy(&verdef, (void *)iter->verdefs->d_buf + offset, sizeof(GElf_Verdef)) != NULL) { 274 if (verdef.vd_ndx != ver) { 275 if (!verdef.vd_next) 276 break; 277 offset += verdef.vd_next; 278 continue; 279 } 280 if(memcpy(&verdaux, (void *)iter->verdefs->d_buf + offset + verdef.vd_aux, sizeof(GElf_Verdaux)) == NULL) { 281 break; 282 } 283 psection_t psection = elfio_get_section_by_index(iter->elf, iter->verdef_strtabidx); 284 if (!psection) 285 return NULL; 286 pstring_t strstring = elfio_string_section_accessor_new(psection); 287 return elfio_string_get_string(strstring, verdaux.vda_name); 288 } 289#endif 290return NULL; 291} 292 293static bool symbol_match(struct elf_sym_iter *iter, int sh_type, struct elf_sym *sym, 294 const char *name, size_t name_len, const char *lib_ver) 295{ 296 const char *ver_name; 297 298 /* Symbols are in forms of func, func@LIB_VER or func@@LIB_VER 299 * make sure the func part matches the user specified name 300 */ 301 if (strncmp(sym->name, name, name_len) != 0) 302 return false; 303 304 /* ...but we don't want a search for "foo" to match 'foo2" also, so any 305 * additional characters in sname should be of the form "@@LIB". 306 */ 307 if (sym->name[name_len] != '\0' && sym->name[name_len] != '@') 308 return false; 309 310 /* If user does not specify symbol version, then we got a match */ 311 if (!lib_ver) 312 return true; 313 314 /* If user specifies symbol version, for dynamic symbols, 315 * get version name from ELF verdef section for comparison. 316 */ 317 if (sh_type == SHT_DYNSYM) { 318 ver_name = elf_get_vername(iter, sym->ver); 319 if (!ver_name) 320 return false; 321 return strcmp(ver_name, lib_ver) == 0; 322 } 323 324 /* For normal symbols, it is already in form of func@LIB_VER */ 325 return strcmp(sym->name, name) == 0; 326} 327 328/* Transform symbol's virtual address (absolute for binaries and relative 329 * for shared libs) into file offset, which is what kernel is expecting 330 * for uprobe/uretprobe attachment. 331 * See Documentation/trace/uprobetracer.rst for more details. This is done 332 * by looking up symbol's containing section's header and using iter's virtual 333 * address (sh_addr) and corresponding file offset (sh_offset) to transform 334 * sym.st_value (virtual address) into desired final file offset. 335 */ 336static unsigned long elf_sym_offset(struct elf_sym *sym) 337{ 338 return sym->sym.st_value - sym->sh.sh_addr + sym->sh.sh_offset; 339} 340 341/* Find offset of function name in the provided ELF object. "binary_path" is 342 * the path to the ELF binary represented by "elf", and only used for error 343 * reporting matters. "name" matches symbol name or name@@LIB for library 344 * functions. 345 */ 346#ifdef HAVE_LIBELF 347long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name) 348#elif HAVE_ELFIO 349long elf_find_func_offset(pelfio_t elf, const char *binary_path, const char *name) 350#endif 351{ 352 int i, sh_types[2] = { SHT_DYNSYM, SHT_SYMTAB }; 353 const char *at_symbol, *lib_ver; 354 bool is_shared_lib; 355 long ret = -ENOENT; 356 size_t name_len; 357#ifdef HAVE_LIBELF 358 GElf_Ehdr ehdr; 359 360 if (!gelf_getehdr(elf, &ehdr)) { 361 pr_warn("elf: failed to get ehdr from %s: %s\n", binary_path, elf_errmsg(-1)); 362 ret = -LIBBPF_ERRNO__FORMAT; 363 goto out; 364 } 365 366 /* for shared lib case, we do not need to calculate relative offset */ 367 is_shared_lib = ehdr.e_type == ET_DYN; 368#elif HAVA_ELFIO 369 is_shared_lib = (ET_DYN == elfio_get_type(pelfio)); 370#endif 371 /* Does name specify "@@LIB_VER" or "@LIB_VER" ? */ 372 at_symbol = strchr(name, '@'); 373 if (at_symbol) { 374 name_len = at_symbol - name; 375 /* skip second @ if it's @@LIB_VER case */ 376 if (at_symbol[1] == '@') 377 at_symbol++; 378 lib_ver = at_symbol + 1; 379 } else { 380 name_len = strlen(name); 381 lib_ver = NULL; 382 } 383 384 /* Search SHT_DYNSYM, SHT_SYMTAB for symbol. This search order is used because if 385 * a binary is stripped, it may only have SHT_DYNSYM, and a fully-statically 386 * linked binary may not have SHT_DYMSYM, so absence of a section should not be 387 * reported as a warning/error. 388 */ 389 for (i = 0; i < ARRAY_SIZE(sh_types); i++) { 390 struct elf_sym_iter iter; 391 struct elf_sym *sym; 392 int last_bind = -1; 393 int cur_bind; 394 395 ret = elf_sym_iter_new(&iter, elf, binary_path, sh_types[i], STT_FUNC); 396 if (ret == -ENOENT) 397 continue; 398 if (ret) 399 goto out; 400 401 while ((sym = elf_sym_iter_next(&iter))) { 402 if (!symbol_match(&iter, sh_types[i], sym, name, name_len, lib_ver)) 403 continue; 404 405 cur_bind = GELF_ST_BIND(sym->sym.st_info); 406 407 if (ret > 0) { 408 /* handle multiple matches */ 409 if (elf_sym_offset(sym) == ret) { 410 /* same offset, no problem */ 411 continue; 412 } else if (last_bind != STB_WEAK && cur_bind != STB_WEAK) { 413 /* Only accept one non-weak bind. */ 414 pr_warn("elf: ambiguous match for '%s', '%s' in '%s'\n", 415 sym->name, name, binary_path); 416 ret = -LIBBPF_ERRNO__FORMAT; 417 goto out; 418 } else if (cur_bind == STB_WEAK) { 419 /* already have a non-weak bind, and 420 * this is a weak bind, so ignore. 421 */ 422 continue; 423 } 424 } 425 426 ret = elf_sym_offset(sym); 427 last_bind = cur_bind; 428 } 429 if (ret > 0) 430 break; 431 } 432 433 if (ret > 0) { 434 pr_debug("elf: symbol address match for '%s' in '%s': 0x%lx\n", name, binary_path, 435 ret); 436 } else { 437 if (ret == 0) { 438 pr_warn("elf: '%s' is 0 in symtab for '%s': %s\n", name, binary_path, 439 is_shared_lib ? "should not be 0 in a shared library" : 440 "try using shared library path instead"); 441 ret = -ENOENT; 442 } else { 443 pr_warn("elf: failed to find symbol '%s' in '%s'\n", name, binary_path); 444 } 445 } 446out: 447 return ret; 448} 449 450/* Find offset of function name in ELF object specified by path. "name" matches 451 * symbol name or name@@LIB for library functions. 452 */ 453long elf_find_func_offset_from_file(const char *binary_path, const char *name) 454{ 455 struct elf_fd elf_fd; 456 long ret = -ENOENT; 457 458 ret = elf_open(binary_path, &elf_fd); 459 if (ret) 460 return ret; 461 ret = elf_find_func_offset(elf_fd.elf, binary_path, name); 462 elf_close(&elf_fd); 463 return ret; 464} 465 466struct symbol { 467 const char *name; 468 int bind; 469 int idx; 470}; 471 472static int symbol_cmp(const void *a, const void *b) 473{ 474 const struct symbol *sym_a = a; 475 const struct symbol *sym_b = b; 476 477 return strcmp(sym_a->name, sym_b->name); 478} 479 480/* 481 * Return offsets in @poffsets for symbols specified in @syms array argument. 482 * On success returns 0 and offsets are returned in allocated array with @cnt 483 * size, that needs to be released by the caller. 484 */ 485int elf_resolve_syms_offsets(const char *binary_path, int cnt, 486 const char **syms, unsigned long **poffsets) 487{ 488 int sh_types[2] = { SHT_DYNSYM, SHT_SYMTAB }; 489 int err = 0, i, cnt_done = 0; 490 unsigned long *offsets; 491 struct symbol *symbols; 492 struct elf_fd elf_fd; 493 494 err = elf_open(binary_path, &elf_fd); 495 if (err) 496 return err; 497 498 offsets = calloc(cnt, sizeof(*offsets)); 499 symbols = calloc(cnt, sizeof(*symbols)); 500 501 if (!offsets || !symbols) { 502 err = -ENOMEM; 503 goto out; 504 } 505 506 for (i = 0; i < cnt; i++) { 507 symbols[i].name = syms[i]; 508 symbols[i].idx = i; 509 } 510 511 qsort(symbols, cnt, sizeof(*symbols), symbol_cmp); 512 513 for (i = 0; i < ARRAY_SIZE(sh_types); i++) { 514 struct elf_sym_iter iter; 515 struct elf_sym *sym; 516 517 err = elf_sym_iter_new(&iter, elf_fd.elf, binary_path, sh_types[i], STT_FUNC); 518 if (err == -ENOENT) 519 continue; 520 if (err) 521 goto out; 522 523 while ((sym = elf_sym_iter_next(&iter))) { 524 unsigned long sym_offset = elf_sym_offset(sym); 525 int bind = GELF_ST_BIND(sym->sym.st_info); 526 struct symbol *found, tmp = { 527 .name = sym->name, 528 }; 529 unsigned long *offset; 530 531 found = bsearch(&tmp, symbols, cnt, sizeof(*symbols), symbol_cmp); 532 if (!found) 533 continue; 534 535 offset = &offsets[found->idx]; 536 if (*offset > 0) { 537 /* same offset, no problem */ 538 if (*offset == sym_offset) 539 continue; 540 /* handle multiple matches */ 541 if (found->bind != STB_WEAK && bind != STB_WEAK) { 542 /* Only accept one non-weak bind. */ 543 pr_warn("elf: ambiguous match found '%s@%lu' in '%s' previous offset %lu\n", 544 sym->name, sym_offset, binary_path, *offset); 545 err = -ESRCH; 546 goto out; 547 } else if (bind == STB_WEAK) { 548 /* already have a non-weak bind, and 549 * this is a weak bind, so ignore. 550 */ 551 continue; 552 } 553 } else { 554 cnt_done++; 555 } 556 *offset = sym_offset; 557 found->bind = bind; 558 } 559 } 560 561 if (cnt != cnt_done) { 562 err = -ENOENT; 563 goto out; 564 } 565 566 *poffsets = offsets; 567 568out: 569 free(symbols); 570 if (err) 571 free(offsets); 572 elf_close(&elf_fd); 573 return err; 574} 575 576/* 577 * Return offsets in @poffsets for symbols specified by @pattern argument. 578 * On success returns 0 and offsets are returned in allocated @poffsets 579 * array with the @pctn size, that needs to be released by the caller. 580 */ 581int elf_resolve_pattern_offsets(const char *binary_path, const char *pattern, 582 unsigned long **poffsets, size_t *pcnt) 583{ 584 int sh_types[2] = { SHT_SYMTAB, SHT_DYNSYM }; 585 unsigned long *offsets = NULL; 586 size_t cap = 0, cnt = 0; 587 struct elf_fd elf_fd; 588 int err = 0, i; 589 590 err = elf_open(binary_path, &elf_fd); 591 if (err) 592 return err; 593 594 for (i = 0; i < ARRAY_SIZE(sh_types); i++) { 595 struct elf_sym_iter iter; 596 struct elf_sym *sym; 597 598 err = elf_sym_iter_new(&iter, elf_fd.elf, binary_path, sh_types[i], STT_FUNC); 599 if (err == -ENOENT) 600 continue; 601 if (err) 602 goto out; 603 604 while ((sym = elf_sym_iter_next(&iter))) { 605 if (!glob_match(sym->name, pattern)) 606 continue; 607 608 err = libbpf_ensure_mem((void **) &offsets, &cap, sizeof(*offsets), 609 cnt + 1); 610 if (err) 611 goto out; 612 613 offsets[cnt++] = elf_sym_offset(sym); 614 } 615 616 /* If we found anything in the first symbol section, 617 * do not search others to avoid duplicates. 618 */ 619 if (cnt) 620 break; 621 } 622 623 if (cnt) { 624 *poffsets = offsets; 625 *pcnt = cnt; 626 } else { 627 err = -ENOENT; 628 } 629 630out: 631 if (err) 632 free(offsets); 633 elf_close(&elf_fd); 634 return err; 635} 636