1 // SPDX-License-Identifier: GPL-2.0 2 #include <ctype.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <assert.h> 7 #include <errno.h> 8 #include <fcntl.h> 9 #include <poll.h> 10 #include <pthread.h> 11 #include <unistd.h> 12 #include <linux/perf_event.h> 13 #include <linux/fs.h> 14 #include <sys/ioctl.h> 15 #include <sys/mman.h> 16 #include "trace_helpers.h" 17 #include <linux/limits.h> 18 #include <libelf.h> 19 #include <gelf.h> 20 #include "bpf/hashmap.h" 21 #include "bpf/libbpf_internal.h" 22 #include "bpf_util.h" 23 24 #define TRACEFS_PIPE "/sys/kernel/tracing/trace_pipe" 25 #define DEBUGFS_PIPE "/sys/kernel/debug/tracing/trace_pipe" 26 27 static struct ksyms *ksyms; 28 static pthread_mutex_t ksyms_mutex = PTHREAD_MUTEX_INITIALIZER; 29 30 static int ksyms__add_symbol(struct ksyms *ksyms, const char *name, 31 unsigned long addr) 32 { 33 void *tmp; 34 35 tmp = strdup(name); 36 if (!tmp) 37 return -ENOMEM; 38 ksyms->syms[ksyms->sym_cnt].addr = addr; 39 ksyms->syms[ksyms->sym_cnt].name = tmp; 40 ksyms->sym_cnt++; 41 return 0; 42 } 43 44 void free_kallsyms_local(struct ksyms *ksyms) 45 { 46 unsigned int i; 47 48 if (!ksyms) 49 return; 50 51 free(ksyms->filtered_syms); 52 53 if (!ksyms->syms) { 54 free(ksyms); 55 return; 56 } 57 58 for (i = 0; i < ksyms->sym_cnt; i++) 59 free(ksyms->syms[i].name); 60 free(ksyms->syms); 61 free(ksyms); 62 } 63 64 static struct ksyms *load_kallsyms_local_common(ksym_cmp_t cmp_cb) 65 { 66 FILE *f; 67 char func[256], buf[256]; 68 char symbol; 69 void *addr; 70 int ret; 71 struct ksyms *ksyms; 72 73 f = fopen("/proc/kallsyms", "r"); 74 if (!f) 75 return NULL; 76 77 ksyms = calloc(1, sizeof(struct ksyms)); 78 if (!ksyms) { 79 fclose(f); 80 return NULL; 81 } 82 83 while (fgets(buf, sizeof(buf), f)) { 84 if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3) 85 break; 86 if (!addr) 87 continue; 88 89 ret = libbpf_ensure_mem((void **) &ksyms->syms, &ksyms->sym_cap, 90 sizeof(struct ksym), ksyms->sym_cnt + 1); 91 if (ret) 92 goto error; 93 ret = ksyms__add_symbol(ksyms, func, (unsigned long)addr); 94 if (ret) 95 goto error; 96 } 97 fclose(f); 98 qsort(ksyms->syms, ksyms->sym_cnt, sizeof(struct ksym), cmp_cb); 99 return ksyms; 100 101 error: 102 fclose(f); 103 free_kallsyms_local(ksyms); 104 return NULL; 105 } 106 107 static int ksym_cmp(const void *p1, const void *p2) 108 { 109 return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr; 110 } 111 112 struct ksyms *load_kallsyms_local(void) 113 { 114 return load_kallsyms_local_common(ksym_cmp); 115 } 116 117 struct ksyms *load_kallsyms_custom_local(ksym_cmp_t cmp_cb) 118 { 119 return load_kallsyms_local_common(cmp_cb); 120 } 121 122 int load_kallsyms(void) 123 { 124 pthread_mutex_lock(&ksyms_mutex); 125 if (!ksyms) 126 ksyms = load_kallsyms_local(); 127 pthread_mutex_unlock(&ksyms_mutex); 128 return ksyms ? 0 : 1; 129 } 130 131 struct ksym *ksym_search_local(struct ksyms *ksyms, long key) 132 { 133 int start = 0, end = ksyms->sym_cnt; 134 int result; 135 136 /* kallsyms not loaded. return NULL */ 137 if (ksyms->sym_cnt <= 0) 138 return NULL; 139 140 while (start < end) { 141 size_t mid = start + (end - start) / 2; 142 143 result = key - ksyms->syms[mid].addr; 144 if (result < 0) 145 end = mid; 146 else if (result > 0) 147 start = mid + 1; 148 else 149 return &ksyms->syms[mid]; 150 } 151 152 if (start >= 1 && ksyms->syms[start - 1].addr < key && 153 key < ksyms->syms[start].addr) 154 /* valid ksym */ 155 return &ksyms->syms[start - 1]; 156 157 /* out of range. return _stext */ 158 return &ksyms->syms[0]; 159 } 160 161 struct ksym *search_kallsyms_custom_local(struct ksyms *ksyms, const void *p, 162 ksym_search_cmp_t cmp_cb) 163 { 164 int start = 0, mid, end = ksyms->sym_cnt; 165 struct ksym *ks; 166 int result; 167 168 while (start < end) { 169 mid = start + (end - start) / 2; 170 ks = &ksyms->syms[mid]; 171 result = cmp_cb(p, ks); 172 if (result < 0) 173 end = mid; 174 else if (result > 0) 175 start = mid + 1; 176 else 177 return ks; 178 } 179 180 return NULL; 181 } 182 183 struct ksym *ksym_search(long key) 184 { 185 if (!ksyms) 186 return NULL; 187 return ksym_search_local(ksyms, key); 188 } 189 190 long ksym_get_addr_local(struct ksyms *ksyms, const char *name) 191 { 192 int i; 193 194 for (i = 0; i < ksyms->sym_cnt; i++) { 195 if (strcmp(ksyms->syms[i].name, name) == 0) 196 return ksyms->syms[i].addr; 197 } 198 199 return 0; 200 } 201 202 long ksym_get_addr(const char *name) 203 { 204 if (!ksyms) 205 return 0; 206 return ksym_get_addr_local(ksyms, name); 207 } 208 209 /* open kallsyms and read symbol addresses on the fly. Without caching all symbols, 210 * this is faster than load + find. 211 */ 212 int kallsyms_find(const char *sym, unsigned long long *addr) 213 { 214 char type, name[500], *match; 215 unsigned long long value; 216 int err = 0; 217 FILE *f; 218 219 f = fopen("/proc/kallsyms", "r"); 220 if (!f) 221 return -EINVAL; 222 223 while (fscanf(f, "%llx %c %499s%*[^\n]\n", &value, &type, name) > 0) { 224 /* If CONFIG_LTO_CLANG_THIN is enabled, static variable/function 225 * symbols could be promoted to global due to cross-file inlining. 226 * For such cases, clang compiler will add .llvm.<hash> suffix 227 * to those symbols to avoid potential naming conflict. 228 * Let us ignore .llvm.<hash> suffix during symbol comparison. 229 */ 230 if (type == 'd') { 231 match = strstr(name, ".llvm."); 232 if (match) 233 *match = '\0'; 234 } 235 if (strcmp(name, sym) == 0) { 236 *addr = value; 237 goto out; 238 } 239 } 240 err = -ENOENT; 241 242 out: 243 fclose(f); 244 return err; 245 } 246 247 unsigned long long module_get_base_addr(const char *module) 248 { 249 unsigned long long addr, base_addr = 0; 250 char name[500]; 251 FILE *f; 252 253 f = fopen("/proc/modules", "r"); 254 if (!f) 255 return 0; 256 257 while (fscanf(f, "%499s %*s %*s %*s %*s 0x%llx", name, &addr) == 2) { 258 if (strcmp(module, name) == 0) { 259 base_addr = addr; 260 break; 261 } 262 } 263 fclose(f); 264 return base_addr; 265 } 266 267 unsigned long long module_get_base_addr_sec(const char *module, const char *sec) 268 { 269 unsigned long long addr = 0; 270 char *path; 271 FILE *f; 272 273 if (asprintf(&path, "/sys/module/%s/sections/%s", module, sec) < 0) 274 return 0; 275 f = fopen(path, "r"); 276 free(path); 277 if (!f) 278 return 0; 279 280 if (fscanf(f, "%llx", &addr) != 1) 281 addr = 0; 282 fclose(f); 283 return addr; 284 } 285 286 #ifdef PROCMAP_QUERY 287 int env_verbosity __weak = 0; 288 289 static int procmap_query(int fd, const void *addr, __u32 query_flags, size_t *start, size_t *offset, int *flags) 290 { 291 char path_buf[PATH_MAX], build_id_buf[20]; 292 struct procmap_query q; 293 int err; 294 295 memset(&q, 0, sizeof(q)); 296 q.size = sizeof(q); 297 q.query_flags = query_flags; 298 q.query_addr = (__u64)addr; 299 q.vma_name_addr = (__u64)path_buf; 300 q.vma_name_size = sizeof(path_buf); 301 q.build_id_addr = (__u64)build_id_buf; 302 q.build_id_size = sizeof(build_id_buf); 303 304 err = ioctl(fd, PROCMAP_QUERY, &q); 305 if (err < 0) { 306 err = -errno; 307 if (err == -ENOTTY) 308 return -EOPNOTSUPP; /* ioctl() not implemented yet */ 309 if (err == -ENOENT) 310 return -ESRCH; /* vma not found */ 311 return err; 312 } 313 314 if (env_verbosity >= 1) { 315 printf("VMA FOUND (addr %08lx): %08lx-%08lx %c%c%c%c %08lx %02x:%02x %ld %s (build ID: %s, %d bytes)\n", 316 (long)addr, (long)q.vma_start, (long)q.vma_end, 317 (q.vma_flags & PROCMAP_QUERY_VMA_READABLE) ? 'r' : '-', 318 (q.vma_flags & PROCMAP_QUERY_VMA_WRITABLE) ? 'w' : '-', 319 (q.vma_flags & PROCMAP_QUERY_VMA_EXECUTABLE) ? 'x' : '-', 320 (q.vma_flags & PROCMAP_QUERY_VMA_SHARED) ? 's' : 'p', 321 (long)q.vma_offset, q.dev_major, q.dev_minor, (long)q.inode, 322 q.vma_name_size ? path_buf : "", 323 q.build_id_size ? "YES" : "NO", 324 q.build_id_size); 325 } 326 327 *start = q.vma_start; 328 *offset = q.vma_offset; 329 *flags = q.vma_flags; 330 return 0; 331 } 332 #else 333 # ifndef PROCMAP_QUERY_VMA_EXECUTABLE 334 # define PROCMAP_QUERY_VMA_EXECUTABLE 0x04 335 # endif 336 337 static int procmap_query(int fd, const void *addr, __u32 query_flags, size_t *start, size_t *offset, int *flags) 338 { 339 return -EOPNOTSUPP; 340 } 341 #endif 342 343 ssize_t get_uprobe_offset(const void *addr) 344 { 345 size_t start, base, end; 346 FILE *f; 347 char buf[256]; 348 int err, flags; 349 350 f = fopen("/proc/self/maps", "r"); 351 if (!f) 352 return -errno; 353 354 /* requested executable VMA only */ 355 err = procmap_query(fileno(f), addr, PROCMAP_QUERY_VMA_EXECUTABLE, &start, &base, &flags); 356 if (err == -EOPNOTSUPP) { 357 bool found = false; 358 359 while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) == 4) { 360 if (buf[2] == 'x' && (uintptr_t)addr >= start && (uintptr_t)addr < end) { 361 found = true; 362 break; 363 } 364 } 365 if (!found) { 366 fclose(f); 367 return -ESRCH; 368 } 369 } else if (err) { 370 fclose(f); 371 return err; 372 } 373 fclose(f); 374 375 #if defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2 376 377 #define OP_RT_RA_MASK 0xffff0000UL 378 #define LIS_R2 0x3c400000UL 379 #define ADDIS_R2_R12 0x3c4c0000UL 380 #define ADDI_R2_R2 0x38420000UL 381 382 /* 383 * A PPC64 ABIv2 function may have a local and a global entry 384 * point. We need to use the local entry point when patching 385 * functions, so identify and step over the global entry point 386 * sequence. 387 * 388 * The global entry point sequence is always of the form: 389 * 390 * addis r2,r12,XXXX 391 * addi r2,r2,XXXX 392 * 393 * A linker optimisation may convert the addis to lis: 394 * 395 * lis r2,XXXX 396 * addi r2,r2,XXXX 397 */ 398 { 399 const __u32 *insn = (const __u32 *)(uintptr_t)addr; 400 401 if ((((*insn & OP_RT_RA_MASK) == ADDIS_R2_R12) || 402 ((*insn & OP_RT_RA_MASK) == LIS_R2)) && 403 ((*(insn + 1) & OP_RT_RA_MASK) == ADDI_R2_R2)) 404 return (uintptr_t)(insn + 2) - start + base; 405 } 406 #endif 407 return (uintptr_t)addr - start + base; 408 } 409 410 ssize_t get_rel_offset(uintptr_t addr) 411 { 412 size_t start, end, offset; 413 char buf[256]; 414 FILE *f; 415 int err, flags; 416 417 f = fopen("/proc/self/maps", "r"); 418 if (!f) 419 return -errno; 420 421 err = procmap_query(fileno(f), (const void *)addr, 0, &start, &offset, &flags); 422 if (err == 0) { 423 fclose(f); 424 return (size_t)addr - start + offset; 425 } else if (err != -EOPNOTSUPP) { 426 fclose(f); 427 return err; 428 } else if (err) { 429 while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &offset) == 4) { 430 if (addr >= start && addr < end) { 431 fclose(f); 432 return (size_t)addr - start + offset; 433 } 434 } 435 } 436 437 fclose(f); 438 return -EINVAL; 439 } 440 441 static int 442 parse_build_id_buf(const void *note_start, Elf32_Word note_size, char *build_id) 443 { 444 Elf32_Word note_offs = 0; 445 446 while (note_offs + sizeof(Elf32_Nhdr) < note_size) { 447 Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs); 448 449 if (nhdr->n_type == 3 && nhdr->n_namesz == sizeof("GNU") && 450 !strcmp((char *)(nhdr + 1), "GNU") && nhdr->n_descsz > 0 && 451 nhdr->n_descsz <= BPF_BUILD_ID_SIZE) { 452 memcpy(build_id, note_start + note_offs + 453 ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr), nhdr->n_descsz); 454 memset(build_id + nhdr->n_descsz, 0, BPF_BUILD_ID_SIZE - nhdr->n_descsz); 455 return (int) nhdr->n_descsz; 456 } 457 458 note_offs = note_offs + sizeof(Elf32_Nhdr) + 459 ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4); 460 } 461 462 return -ENOENT; 463 } 464 465 /* Reads binary from *path* file and returns it in the *build_id* buffer 466 * with *size* which is expected to be at least BPF_BUILD_ID_SIZE bytes. 467 * Returns size of build id on success. On error the error value is 468 * returned. 469 */ 470 int read_build_id(const char *path, char *build_id, size_t size) 471 { 472 int fd, err = -EINVAL; 473 Elf *elf = NULL; 474 GElf_Ehdr ehdr; 475 size_t max, i; 476 477 if (size < BPF_BUILD_ID_SIZE) 478 return -EINVAL; 479 480 fd = open(path, O_RDONLY | O_CLOEXEC); 481 if (fd < 0) 482 return -errno; 483 484 (void)elf_version(EV_CURRENT); 485 486 elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); 487 if (!elf) 488 goto out; 489 if (elf_kind(elf) != ELF_K_ELF) 490 goto out; 491 if (!gelf_getehdr(elf, &ehdr)) 492 goto out; 493 494 for (i = 0; i < ehdr.e_phnum; i++) { 495 GElf_Phdr mem, *phdr; 496 char *data; 497 498 phdr = gelf_getphdr(elf, i, &mem); 499 if (!phdr) 500 goto out; 501 if (phdr->p_type != PT_NOTE) 502 continue; 503 data = elf_rawfile(elf, &max); 504 if (!data) 505 goto out; 506 if (phdr->p_offset + phdr->p_memsz > max) 507 goto out; 508 err = parse_build_id_buf(data + phdr->p_offset, phdr->p_memsz, build_id); 509 if (err > 0) 510 break; 511 } 512 513 out: 514 if (elf) 515 elf_end(elf); 516 close(fd); 517 return err; 518 } 519 520 int read_trace_pipe_iter(void (*cb)(const char *str, void *data), void *data, int iter) 521 { 522 size_t buflen, n; 523 char *buf = NULL; 524 FILE *fp = NULL; 525 526 if (access(TRACEFS_PIPE, F_OK) == 0) 527 fp = fopen(TRACEFS_PIPE, "r"); 528 else 529 fp = fopen(DEBUGFS_PIPE, "r"); 530 if (!fp) 531 return -1; 532 533 /* We do not want to wait forever when iter is specified. */ 534 if (iter) 535 fcntl(fileno(fp), F_SETFL, O_NONBLOCK); 536 537 while ((n = getline(&buf, &buflen, fp) >= 0) || errno == EAGAIN) { 538 if (n > 0) 539 cb(buf, data); 540 if (iter && !(--iter)) 541 break; 542 } 543 544 free(buf); 545 if (fp) 546 fclose(fp); 547 return 0; 548 } 549 550 static void trace_pipe_cb(const char *str, void *data) 551 { 552 printf("%s", str); 553 } 554 555 void read_trace_pipe(void) 556 { 557 read_trace_pipe_iter(trace_pipe_cb, NULL, 0); 558 } 559 560 static size_t symbol_hash(long key, void *ctx __maybe_unused) 561 { 562 return str_hash((const char *) key); 563 } 564 565 static bool symbol_equal(long key1, long key2, void *ctx __maybe_unused) 566 { 567 return strcmp((const char *) key1, (const char *) key2) == 0; 568 } 569 570 static bool is_invalid_entry(char *buf, bool kernel) 571 { 572 if (kernel && strchr(buf, '[')) 573 return true; 574 if (!kernel && !strchr(buf, '[')) 575 return true; 576 return false; 577 } 578 579 static const char * const trace_blacklist[] = { 580 "migrate_disable", 581 "migrate_enable", 582 "rcu_read_unlock_strict", 583 "preempt_count_add", 584 "preempt_count_sub", 585 "__rcu_read_lock", 586 "__rcu_read_unlock", 587 "bpf_get_numa_node_id", 588 "___migrate_enable", 589 }; 590 591 bool is_unsafe_function(const char *name) 592 { 593 int i; 594 595 /* 596 * We attach to almost all kernel functions and some of them 597 * will cause 'suspicious RCU usage' when fprobe is attached 598 * to them. Filter out the current culprits - arch_cpu_idle 599 * default_idle and rcu_* functions. 600 */ 601 if (!strcmp(name, "arch_cpu_idle")) 602 return true; 603 if (!strcmp(name, "default_idle")) 604 return true; 605 if (!strncmp(name, "rcu_", 4)) 606 return true; 607 if (!strcmp(name, "bpf_dispatcher_xdp_func")) 608 return true; 609 if (!strncmp(name, "__ftrace_invalid_address__", 610 sizeof("__ftrace_invalid_address__") - 1)) 611 return true; 612 613 for (i = 0; i < ARRAY_SIZE(trace_blacklist); i++) { 614 if (!strcmp(name, trace_blacklist[i])) 615 return true; 616 } 617 618 return false; 619 } 620 621 /* Do comparison by ignoring '.llvm.<hash>' suffixes. */ 622 static int compare_name(const char *name1, const char *name2) 623 { 624 const char *res1, *res2; 625 int len1, len2; 626 627 res1 = strstr(name1, ".llvm."); 628 res2 = strstr(name2, ".llvm."); 629 len1 = res1 ? res1 - name1 : strlen(name1); 630 len2 = res2 ? res2 - name2 : strlen(name2); 631 632 if (len1 == len2) 633 return strncmp(name1, name2, len1); 634 if (len1 < len2) 635 return strncmp(name1, name2, len1) <= 0 ? -1 : 1; 636 return strncmp(name1, name2, len2) >= 0 ? 1 : -1; 637 } 638 639 static int load_kallsyms_compare(const void *p1, const void *p2) 640 { 641 return compare_name(((const struct ksym *)p1)->name, ((const struct ksym *)p2)->name); 642 } 643 644 static int search_kallsyms_compare(const void *p1, const struct ksym *p2) 645 { 646 return compare_name(p1, p2->name); 647 } 648 649 int bpf_get_ksyms(struct ksyms **ksymsp, bool kernel) 650 { 651 size_t cap = 0, cnt = 0; 652 char *name = NULL, *ksym_name, **syms = NULL; 653 struct hashmap *map; 654 struct ksyms *ksyms; 655 struct ksym *ks; 656 char buf[256]; 657 FILE *f; 658 int err = 0; 659 660 ksyms = load_kallsyms_custom_local(load_kallsyms_compare); 661 if (!ksyms) 662 return -EINVAL; 663 664 /* 665 * The available_filter_functions contains many duplicates, 666 * but other than that all symbols are usable to trace. 667 * Filtering out duplicates by using hashmap__add, which won't 668 * add existing entry. 669 */ 670 671 if (access("/sys/kernel/tracing/trace", F_OK) == 0) 672 f = fopen("/sys/kernel/tracing/available_filter_functions", "r"); 673 else 674 f = fopen("/sys/kernel/debug/tracing/available_filter_functions", "r"); 675 676 if (!f) { 677 free_kallsyms_local(ksyms); 678 return -EINVAL; 679 } 680 681 map = hashmap__new(symbol_hash, symbol_equal, NULL); 682 if (IS_ERR(map)) { 683 err = libbpf_get_error(map); 684 goto error; 685 } 686 687 while (fgets(buf, sizeof(buf), f)) { 688 if (is_invalid_entry(buf, kernel)) 689 continue; 690 691 free(name); 692 if (sscanf(buf, "%ms$*[^\n]\n", &name) != 1) 693 continue; 694 if (is_unsafe_function(name)) 695 continue; 696 697 ks = search_kallsyms_custom_local(ksyms, name, search_kallsyms_compare); 698 if (!ks) { 699 err = -EINVAL; 700 goto error; 701 } 702 703 ksym_name = ks->name; 704 err = hashmap__add(map, ksym_name, 0); 705 if (err == -EEXIST) { 706 err = 0; 707 continue; 708 } 709 if (err) 710 goto error; 711 712 err = libbpf_ensure_mem((void **) &syms, &cap, 713 sizeof(*syms), cnt + 1); 714 if (err) 715 goto error; 716 717 syms[cnt++] = ksym_name; 718 } 719 720 ksyms->filtered_syms = syms; 721 ksyms->filtered_cnt = cnt; 722 *ksymsp = ksyms; 723 724 error: 725 free(name); 726 fclose(f); 727 hashmap__free(map); 728 if (err) { 729 free(syms); 730 free_kallsyms_local(ksyms); 731 } 732 return err; 733 } 734 735 int bpf_get_addrs(unsigned long **addrsp, size_t *cntp, bool kernel) 736 { 737 unsigned long *addr, *addrs, *tmp_addrs; 738 int err = 0, max_cnt, inc_cnt; 739 char *name = NULL; 740 size_t cnt = 0; 741 char buf[256]; 742 FILE *f; 743 744 if (access("/sys/kernel/tracing/trace", F_OK) == 0) 745 f = fopen("/sys/kernel/tracing/available_filter_functions_addrs", "r"); 746 else 747 f = fopen("/sys/kernel/debug/tracing/available_filter_functions_addrs", "r"); 748 749 if (!f) 750 return -ENOENT; 751 752 /* In my local setup, the number of entries is 50k+ so Let us initially 753 * allocate space to hold 64k entries. If 64k is not enough, incrementally 754 * increase 1k each time. 755 */ 756 max_cnt = 65536; 757 inc_cnt = 1024; 758 addrs = malloc(max_cnt * sizeof(long)); 759 if (addrs == NULL) { 760 err = -ENOMEM; 761 goto error; 762 } 763 764 while (fgets(buf, sizeof(buf), f)) { 765 if (is_invalid_entry(buf, kernel)) 766 continue; 767 768 free(name); 769 if (sscanf(buf, "%p %ms$*[^\n]\n", &addr, &name) != 2) 770 continue; 771 if (is_unsafe_function(name)) 772 continue; 773 774 if (cnt == max_cnt) { 775 max_cnt += inc_cnt; 776 tmp_addrs = realloc(addrs, max_cnt * sizeof(long)); 777 if (!tmp_addrs) { 778 err = -ENOMEM; 779 goto error; 780 } 781 addrs = tmp_addrs; 782 } 783 784 addrs[cnt++] = (unsigned long)addr; 785 } 786 787 *addrsp = addrs; 788 *cntp = cnt; 789 790 error: 791 free(name); 792 fclose(f); 793 if (err) 794 free(addrs); 795 return err; 796 } 797