0001
0002
0003 #include <ctype.h>
0004 #include <stdio.h>
0005 #include <stdlib.h>
0006 #include <string.h>
0007 #include <libelf.h>
0008 #include <gelf.h>
0009 #include <unistd.h>
0010 #include <linux/ptrace.h>
0011 #include <linux/kernel.h>
0012
0013
0014 #if defined(__riscv)
0015 #define rv_s8 s8
0016 #endif
0017
0018 #include "bpf.h"
0019 #include "libbpf.h"
0020 #include "libbpf_common.h"
0021 #include "libbpf_internal.h"
0022 #include "hashmap.h"
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161
0162
0163
0164
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191 #define USDT_BASE_SEC ".stapsdt.base"
0192 #define USDT_SEMA_SEC ".probes"
0193 #define USDT_NOTE_SEC ".note.stapsdt"
0194 #define USDT_NOTE_TYPE 3
0195 #define USDT_NOTE_NAME "stapsdt"
0196
0197
0198 enum usdt_arg_type {
0199 USDT_ARG_CONST,
0200 USDT_ARG_REG,
0201 USDT_ARG_REG_DEREF,
0202 };
0203
0204
0205 struct usdt_arg_spec {
0206 __u64 val_off;
0207 enum usdt_arg_type arg_type;
0208 short reg_off;
0209 bool arg_signed;
0210 char arg_bitshift;
0211 };
0212
0213
0214 #define USDT_MAX_ARG_CNT 12
0215
0216
0217 struct usdt_spec {
0218 struct usdt_arg_spec args[USDT_MAX_ARG_CNT];
0219 __u64 usdt_cookie;
0220 short arg_cnt;
0221 };
0222
0223 struct usdt_note {
0224 const char *provider;
0225 const char *name;
0226
0227
0228
0229 const char *args;
0230 long loc_addr;
0231 long base_addr;
0232 long sema_addr;
0233 };
0234
0235 struct usdt_target {
0236 long abs_ip;
0237 long rel_ip;
0238 long sema_off;
0239 struct usdt_spec spec;
0240 const char *spec_str;
0241 };
0242
0243 struct usdt_manager {
0244 struct bpf_map *specs_map;
0245 struct bpf_map *ip_to_spec_id_map;
0246
0247 int *free_spec_ids;
0248 size_t free_spec_cnt;
0249 size_t next_free_spec_id;
0250
0251 bool has_bpf_cookie;
0252 bool has_sema_refcnt;
0253 };
0254
0255 struct usdt_manager *usdt_manager_new(struct bpf_object *obj)
0256 {
0257 static const char *ref_ctr_sysfs_path = "/sys/bus/event_source/devices/uprobe/format/ref_ctr_offset";
0258 struct usdt_manager *man;
0259 struct bpf_map *specs_map, *ip_to_spec_id_map;
0260
0261 specs_map = bpf_object__find_map_by_name(obj, "__bpf_usdt_specs");
0262 ip_to_spec_id_map = bpf_object__find_map_by_name(obj, "__bpf_usdt_ip_to_spec_id");
0263 if (!specs_map || !ip_to_spec_id_map) {
0264 pr_warn("usdt: failed to find USDT support BPF maps, did you forget to include bpf/usdt.bpf.h?\n");
0265 return ERR_PTR(-ESRCH);
0266 }
0267
0268 man = calloc(1, sizeof(*man));
0269 if (!man)
0270 return ERR_PTR(-ENOMEM);
0271
0272 man->specs_map = specs_map;
0273 man->ip_to_spec_id_map = ip_to_spec_id_map;
0274
0275
0276
0277
0278
0279 man->has_bpf_cookie = kernel_supports(obj, FEAT_BPF_COOKIE);
0280
0281
0282
0283
0284
0285 man->has_sema_refcnt = access(ref_ctr_sysfs_path, F_OK) == 0;
0286
0287 return man;
0288 }
0289
0290 void usdt_manager_free(struct usdt_manager *man)
0291 {
0292 if (IS_ERR_OR_NULL(man))
0293 return;
0294
0295 free(man->free_spec_ids);
0296 free(man);
0297 }
0298
0299 static int sanity_check_usdt_elf(Elf *elf, const char *path)
0300 {
0301 GElf_Ehdr ehdr;
0302 int endianness;
0303
0304 if (elf_kind(elf) != ELF_K_ELF) {
0305 pr_warn("usdt: unrecognized ELF kind %d for '%s'\n", elf_kind(elf), path);
0306 return -EBADF;
0307 }
0308
0309 switch (gelf_getclass(elf)) {
0310 case ELFCLASS64:
0311 if (sizeof(void *) != 8) {
0312 pr_warn("usdt: attaching to 64-bit ELF binary '%s' is not supported\n", path);
0313 return -EBADF;
0314 }
0315 break;
0316 case ELFCLASS32:
0317 if (sizeof(void *) != 4) {
0318 pr_warn("usdt: attaching to 32-bit ELF binary '%s' is not supported\n", path);
0319 return -EBADF;
0320 }
0321 break;
0322 default:
0323 pr_warn("usdt: unsupported ELF class for '%s'\n", path);
0324 return -EBADF;
0325 }
0326
0327 if (!gelf_getehdr(elf, &ehdr))
0328 return -EINVAL;
0329
0330 if (ehdr.e_type != ET_EXEC && ehdr.e_type != ET_DYN) {
0331 pr_warn("usdt: unsupported type of ELF binary '%s' (%d), only ET_EXEC and ET_DYN are supported\n",
0332 path, ehdr.e_type);
0333 return -EBADF;
0334 }
0335
0336 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
0337 endianness = ELFDATA2LSB;
0338 #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
0339 endianness = ELFDATA2MSB;
0340 #else
0341 # error "Unrecognized __BYTE_ORDER__"
0342 #endif
0343 if (endianness != ehdr.e_ident[EI_DATA]) {
0344 pr_warn("usdt: ELF endianness mismatch for '%s'\n", path);
0345 return -EBADF;
0346 }
0347
0348 return 0;
0349 }
0350
0351 static int find_elf_sec_by_name(Elf *elf, const char *sec_name, GElf_Shdr *shdr, Elf_Scn **scn)
0352 {
0353 Elf_Scn *sec = NULL;
0354 size_t shstrndx;
0355
0356 if (elf_getshdrstrndx(elf, &shstrndx))
0357 return -EINVAL;
0358
0359
0360 if (!elf_rawdata(elf_getscn(elf, shstrndx), NULL))
0361 return -EINVAL;
0362
0363 while ((sec = elf_nextscn(elf, sec)) != NULL) {
0364 char *name;
0365
0366 if (!gelf_getshdr(sec, shdr))
0367 return -EINVAL;
0368
0369 name = elf_strptr(elf, shstrndx, shdr->sh_name);
0370 if (name && strcmp(sec_name, name) == 0) {
0371 *scn = sec;
0372 return 0;
0373 }
0374 }
0375
0376 return -ENOENT;
0377 }
0378
0379 struct elf_seg {
0380 long start;
0381 long end;
0382 long offset;
0383 bool is_exec;
0384 };
0385
0386 static int cmp_elf_segs(const void *_a, const void *_b)
0387 {
0388 const struct elf_seg *a = _a;
0389 const struct elf_seg *b = _b;
0390
0391 return a->start < b->start ? -1 : 1;
0392 }
0393
0394 static int parse_elf_segs(Elf *elf, const char *path, struct elf_seg **segs, size_t *seg_cnt)
0395 {
0396 GElf_Phdr phdr;
0397 size_t n;
0398 int i, err;
0399 struct elf_seg *seg;
0400 void *tmp;
0401
0402 *seg_cnt = 0;
0403
0404 if (elf_getphdrnum(elf, &n)) {
0405 err = -errno;
0406 return err;
0407 }
0408
0409 for (i = 0; i < n; i++) {
0410 if (!gelf_getphdr(elf, i, &phdr)) {
0411 err = -errno;
0412 return err;
0413 }
0414
0415 pr_debug("usdt: discovered PHDR #%d in '%s': vaddr 0x%lx memsz 0x%lx offset 0x%lx type 0x%lx flags 0x%lx\n",
0416 i, path, (long)phdr.p_vaddr, (long)phdr.p_memsz, (long)phdr.p_offset,
0417 (long)phdr.p_type, (long)phdr.p_flags);
0418 if (phdr.p_type != PT_LOAD)
0419 continue;
0420
0421 tmp = libbpf_reallocarray(*segs, *seg_cnt + 1, sizeof(**segs));
0422 if (!tmp)
0423 return -ENOMEM;
0424
0425 *segs = tmp;
0426 seg = *segs + *seg_cnt;
0427 (*seg_cnt)++;
0428
0429 seg->start = phdr.p_vaddr;
0430 seg->end = phdr.p_vaddr + phdr.p_memsz;
0431 seg->offset = phdr.p_offset;
0432 seg->is_exec = phdr.p_flags & PF_X;
0433 }
0434
0435 if (*seg_cnt == 0) {
0436 pr_warn("usdt: failed to find PT_LOAD program headers in '%s'\n", path);
0437 return -ESRCH;
0438 }
0439
0440 qsort(*segs, *seg_cnt, sizeof(**segs), cmp_elf_segs);
0441 return 0;
0442 }
0443
0444 static int parse_vma_segs(int pid, const char *lib_path, struct elf_seg **segs, size_t *seg_cnt)
0445 {
0446 char path[PATH_MAX], line[PATH_MAX], mode[16];
0447 size_t seg_start, seg_end, seg_off;
0448 struct elf_seg *seg;
0449 int tmp_pid, i, err;
0450 FILE *f;
0451
0452 *seg_cnt = 0;
0453
0454
0455
0456
0457
0458 if (sscanf(lib_path, "/proc/%d/root%s", &tmp_pid, path) == 2 && pid == tmp_pid)
0459 goto proceed;
0460
0461 if (!realpath(lib_path, path)) {
0462 pr_warn("usdt: failed to get absolute path of '%s' (err %d), using path as is...\n",
0463 lib_path, -errno);
0464 libbpf_strlcpy(path, lib_path, sizeof(path));
0465 }
0466
0467 proceed:
0468 sprintf(line, "/proc/%d/maps", pid);
0469 f = fopen(line, "r");
0470 if (!f) {
0471 err = -errno;
0472 pr_warn("usdt: failed to open '%s' to get base addr of '%s': %d\n",
0473 line, lib_path, err);
0474 return err;
0475 }
0476
0477
0478
0479
0480
0481
0482
0483 while (fscanf(f, "%zx-%zx %s %zx %*s %*d%[^\n]\n",
0484 &seg_start, &seg_end, mode, &seg_off, line) == 5) {
0485 void *tmp;
0486
0487
0488
0489
0490
0491 i = 0;
0492 while (isblank(line[i]))
0493 i++;
0494 if (strcmp(line + i, path) != 0)
0495 continue;
0496
0497 pr_debug("usdt: discovered segment for lib '%s': addrs %zx-%zx mode %s offset %zx\n",
0498 path, seg_start, seg_end, mode, seg_off);
0499
0500
0501 if (mode[2] != 'x')
0502 continue;
0503
0504 tmp = libbpf_reallocarray(*segs, *seg_cnt + 1, sizeof(**segs));
0505 if (!tmp) {
0506 err = -ENOMEM;
0507 goto err_out;
0508 }
0509
0510 *segs = tmp;
0511 seg = *segs + *seg_cnt;
0512 *seg_cnt += 1;
0513
0514 seg->start = seg_start;
0515 seg->end = seg_end;
0516 seg->offset = seg_off;
0517 seg->is_exec = true;
0518 }
0519
0520 if (*seg_cnt == 0) {
0521 pr_warn("usdt: failed to find '%s' (resolved to '%s') within PID %d memory mappings\n",
0522 lib_path, path, pid);
0523 err = -ESRCH;
0524 goto err_out;
0525 }
0526
0527 qsort(*segs, *seg_cnt, sizeof(**segs), cmp_elf_segs);
0528 err = 0;
0529 err_out:
0530 fclose(f);
0531 return err;
0532 }
0533
0534 static struct elf_seg *find_elf_seg(struct elf_seg *segs, size_t seg_cnt, long virtaddr)
0535 {
0536 struct elf_seg *seg;
0537 int i;
0538
0539
0540
0541
0542
0543 for (i = 0, seg = segs; i < seg_cnt; i++, seg++) {
0544 if (seg->start <= virtaddr && virtaddr < seg->end)
0545 return seg;
0546 }
0547 return NULL;
0548 }
0549
0550 static struct elf_seg *find_vma_seg(struct elf_seg *segs, size_t seg_cnt, long offset)
0551 {
0552 struct elf_seg *seg;
0553 int i;
0554
0555
0556
0557
0558
0559 for (i = 0, seg = segs; i < seg_cnt; i++, seg++) {
0560 if (seg->offset <= offset && offset < seg->offset + (seg->end - seg->start))
0561 return seg;
0562 }
0563 return NULL;
0564 }
0565
0566 static int parse_usdt_note(Elf *elf, const char *path, GElf_Nhdr *nhdr,
0567 const char *data, size_t name_off, size_t desc_off,
0568 struct usdt_note *usdt_note);
0569
0570 static int parse_usdt_spec(struct usdt_spec *spec, const struct usdt_note *note, __u64 usdt_cookie);
0571
0572 static int collect_usdt_targets(struct usdt_manager *man, Elf *elf, const char *path, pid_t pid,
0573 const char *usdt_provider, const char *usdt_name, __u64 usdt_cookie,
0574 struct usdt_target **out_targets, size_t *out_target_cnt)
0575 {
0576 size_t off, name_off, desc_off, seg_cnt = 0, vma_seg_cnt = 0, target_cnt = 0;
0577 struct elf_seg *segs = NULL, *vma_segs = NULL;
0578 struct usdt_target *targets = NULL, *target;
0579 long base_addr = 0;
0580 Elf_Scn *notes_scn, *base_scn;
0581 GElf_Shdr base_shdr, notes_shdr;
0582 GElf_Ehdr ehdr;
0583 GElf_Nhdr nhdr;
0584 Elf_Data *data;
0585 int err;
0586
0587 *out_targets = NULL;
0588 *out_target_cnt = 0;
0589
0590 err = find_elf_sec_by_name(elf, USDT_NOTE_SEC, ¬es_shdr, ¬es_scn);
0591 if (err) {
0592 pr_warn("usdt: no USDT notes section (%s) found in '%s'\n", USDT_NOTE_SEC, path);
0593 return err;
0594 }
0595
0596 if (notes_shdr.sh_type != SHT_NOTE || !gelf_getehdr(elf, &ehdr)) {
0597 pr_warn("usdt: invalid USDT notes section (%s) in '%s'\n", USDT_NOTE_SEC, path);
0598 return -EINVAL;
0599 }
0600
0601 err = parse_elf_segs(elf, path, &segs, &seg_cnt);
0602 if (err) {
0603 pr_warn("usdt: failed to process ELF program segments for '%s': %d\n", path, err);
0604 goto err_out;
0605 }
0606
0607
0608
0609
0610 if (find_elf_sec_by_name(elf, USDT_BASE_SEC, &base_shdr, &base_scn) == 0)
0611 base_addr = base_shdr.sh_addr;
0612
0613 data = elf_getdata(notes_scn, 0);
0614 off = 0;
0615 while ((off = gelf_getnote(data, off, &nhdr, &name_off, &desc_off)) > 0) {
0616 long usdt_abs_ip, usdt_rel_ip, usdt_sema_off = 0;
0617 struct usdt_note note;
0618 struct elf_seg *seg = NULL;
0619 void *tmp;
0620
0621 err = parse_usdt_note(elf, path, &nhdr, data->d_buf, name_off, desc_off, ¬e);
0622 if (err)
0623 goto err_out;
0624
0625 if (strcmp(note.provider, usdt_provider) != 0 || strcmp(note.name, usdt_name) != 0)
0626 continue;
0627
0628
0629
0630
0631
0632
0633
0634
0635
0636
0637
0638
0639
0640
0641
0642
0643
0644
0645
0646
0647
0648
0649
0650
0651
0652
0653
0654
0655 usdt_abs_ip = note.loc_addr;
0656 if (base_addr)
0657 usdt_abs_ip += base_addr - note.base_addr;
0658
0659
0660
0661
0662
0663
0664 seg = find_elf_seg(segs, seg_cnt, usdt_abs_ip);
0665 if (!seg) {
0666 err = -ESRCH;
0667 pr_warn("usdt: failed to find ELF program segment for '%s:%s' in '%s' at IP 0x%lx\n",
0668 usdt_provider, usdt_name, path, usdt_abs_ip);
0669 goto err_out;
0670 }
0671 if (!seg->is_exec) {
0672 err = -ESRCH;
0673 pr_warn("usdt: matched ELF binary '%s' segment [0x%lx, 0x%lx) for '%s:%s' at IP 0x%lx is not executable\n",
0674 path, seg->start, seg->end, usdt_provider, usdt_name,
0675 usdt_abs_ip);
0676 goto err_out;
0677 }
0678
0679 usdt_rel_ip = usdt_abs_ip - seg->start + seg->offset;
0680
0681 if (ehdr.e_type == ET_DYN && !man->has_bpf_cookie) {
0682
0683
0684
0685
0686
0687
0688
0689
0690
0691
0692
0693
0694
0695 if (pid < 0) {
0696 pr_warn("usdt: attaching to shared libraries without specific PID is not supported on current kernel\n");
0697 err = -ENOTSUP;
0698 goto err_out;
0699 }
0700
0701
0702 if (vma_seg_cnt == 0) {
0703 err = parse_vma_segs(pid, path, &vma_segs, &vma_seg_cnt);
0704 if (err) {
0705 pr_warn("usdt: failed to get memory segments in PID %d for shared library '%s': %d\n",
0706 pid, path, err);
0707 goto err_out;
0708 }
0709 }
0710
0711 seg = find_vma_seg(vma_segs, vma_seg_cnt, usdt_rel_ip);
0712 if (!seg) {
0713 err = -ESRCH;
0714 pr_warn("usdt: failed to find shared lib memory segment for '%s:%s' in '%s' at relative IP 0x%lx\n",
0715 usdt_provider, usdt_name, path, usdt_rel_ip);
0716 goto err_out;
0717 }
0718
0719 usdt_abs_ip = seg->start - seg->offset + usdt_rel_ip;
0720 }
0721
0722 pr_debug("usdt: probe for '%s:%s' in %s '%s': addr 0x%lx base 0x%lx (resolved abs_ip 0x%lx rel_ip 0x%lx) args '%s' in segment [0x%lx, 0x%lx) at offset 0x%lx\n",
0723 usdt_provider, usdt_name, ehdr.e_type == ET_EXEC ? "exec" : "lib ", path,
0724 note.loc_addr, note.base_addr, usdt_abs_ip, usdt_rel_ip, note.args,
0725 seg ? seg->start : 0, seg ? seg->end : 0, seg ? seg->offset : 0);
0726
0727
0728 if (note.sema_addr) {
0729 if (!man->has_sema_refcnt) {
0730 pr_warn("usdt: kernel doesn't support USDT semaphore refcounting for '%s:%s' in '%s'\n",
0731 usdt_provider, usdt_name, path);
0732 err = -ENOTSUP;
0733 goto err_out;
0734 }
0735
0736 seg = find_elf_seg(segs, seg_cnt, note.sema_addr);
0737 if (!seg) {
0738 err = -ESRCH;
0739 pr_warn("usdt: failed to find ELF loadable segment with semaphore of '%s:%s' in '%s' at 0x%lx\n",
0740 usdt_provider, usdt_name, path, note.sema_addr);
0741 goto err_out;
0742 }
0743 if (seg->is_exec) {
0744 err = -ESRCH;
0745 pr_warn("usdt: matched ELF binary '%s' segment [0x%lx, 0x%lx] for semaphore of '%s:%s' at 0x%lx is executable\n",
0746 path, seg->start, seg->end, usdt_provider, usdt_name,
0747 note.sema_addr);
0748 goto err_out;
0749 }
0750
0751 usdt_sema_off = note.sema_addr - seg->start + seg->offset;
0752
0753 pr_debug("usdt: sema for '%s:%s' in %s '%s': addr 0x%lx base 0x%lx (resolved 0x%lx) in segment [0x%lx, 0x%lx] at offset 0x%lx\n",
0754 usdt_provider, usdt_name, ehdr.e_type == ET_EXEC ? "exec" : "lib ",
0755 path, note.sema_addr, note.base_addr, usdt_sema_off,
0756 seg->start, seg->end, seg->offset);
0757 }
0758
0759
0760 tmp = libbpf_reallocarray(targets, target_cnt + 1, sizeof(*targets));
0761 if (!tmp) {
0762 err = -ENOMEM;
0763 goto err_out;
0764 }
0765 targets = tmp;
0766
0767 target = &targets[target_cnt];
0768 memset(target, 0, sizeof(*target));
0769
0770 target->abs_ip = usdt_abs_ip;
0771 target->rel_ip = usdt_rel_ip;
0772 target->sema_off = usdt_sema_off;
0773
0774
0775
0776
0777 target->spec_str = note.args;
0778
0779 err = parse_usdt_spec(&target->spec, ¬e, usdt_cookie);
0780 if (err)
0781 goto err_out;
0782
0783 target_cnt++;
0784 }
0785
0786 *out_targets = targets;
0787 *out_target_cnt = target_cnt;
0788 err = target_cnt;
0789
0790 err_out:
0791 free(segs);
0792 free(vma_segs);
0793 if (err < 0)
0794 free(targets);
0795 return err;
0796 }
0797
0798 struct bpf_link_usdt {
0799 struct bpf_link link;
0800
0801 struct usdt_manager *usdt_man;
0802
0803 size_t spec_cnt;
0804 int *spec_ids;
0805
0806 size_t uprobe_cnt;
0807 struct {
0808 long abs_ip;
0809 struct bpf_link *link;
0810 } *uprobes;
0811 };
0812
0813 static int bpf_link_usdt_detach(struct bpf_link *link)
0814 {
0815 struct bpf_link_usdt *usdt_link = container_of(link, struct bpf_link_usdt, link);
0816 struct usdt_manager *man = usdt_link->usdt_man;
0817 int i;
0818
0819 for (i = 0; i < usdt_link->uprobe_cnt; i++) {
0820
0821 bpf_link__destroy(usdt_link->uprobes[i].link);
0822
0823
0824
0825
0826
0827
0828 if (!man->has_bpf_cookie) {
0829
0830 (void)bpf_map_delete_elem(bpf_map__fd(man->ip_to_spec_id_map),
0831 &usdt_link->uprobes[i].abs_ip);
0832 }
0833 }
0834
0835
0836
0837
0838 if (!man->free_spec_ids) {
0839
0840 man->free_spec_ids = usdt_link->spec_ids;
0841 man->free_spec_cnt = usdt_link->spec_cnt;
0842 usdt_link->spec_ids = NULL;
0843 } else {
0844
0845 size_t new_cnt = man->free_spec_cnt + usdt_link->spec_cnt;
0846 int *new_free_ids;
0847
0848 new_free_ids = libbpf_reallocarray(man->free_spec_ids, new_cnt,
0849 sizeof(*new_free_ids));
0850
0851
0852
0853
0854
0855
0856 if (new_free_ids) {
0857 memcpy(new_free_ids + man->free_spec_cnt, usdt_link->spec_ids,
0858 usdt_link->spec_cnt * sizeof(*usdt_link->spec_ids));
0859 man->free_spec_ids = new_free_ids;
0860 man->free_spec_cnt = new_cnt;
0861 }
0862 }
0863
0864 return 0;
0865 }
0866
0867 static void bpf_link_usdt_dealloc(struct bpf_link *link)
0868 {
0869 struct bpf_link_usdt *usdt_link = container_of(link, struct bpf_link_usdt, link);
0870
0871 free(usdt_link->spec_ids);
0872 free(usdt_link->uprobes);
0873 free(usdt_link);
0874 }
0875
0876 static size_t specs_hash_fn(const void *key, void *ctx)
0877 {
0878 const char *s = key;
0879
0880 return str_hash(s);
0881 }
0882
0883 static bool specs_equal_fn(const void *key1, const void *key2, void *ctx)
0884 {
0885 const char *s1 = key1;
0886 const char *s2 = key2;
0887
0888 return strcmp(s1, s2) == 0;
0889 }
0890
0891 static int allocate_spec_id(struct usdt_manager *man, struct hashmap *specs_hash,
0892 struct bpf_link_usdt *link, struct usdt_target *target,
0893 int *spec_id, bool *is_new)
0894 {
0895 void *tmp;
0896 int err;
0897
0898
0899 if (hashmap__find(specs_hash, target->spec_str, &tmp)) {
0900 *spec_id = (long)tmp;
0901 *is_new = false;
0902 return 0;
0903 }
0904
0905
0906
0907
0908 tmp = libbpf_reallocarray(link->spec_ids, link->spec_cnt + 1, sizeof(*link->spec_ids));
0909 if (!tmp)
0910 return -ENOMEM;
0911 link->spec_ids = tmp;
0912
0913
0914 if (man->free_spec_cnt) {
0915 *spec_id = man->free_spec_ids[man->free_spec_cnt - 1];
0916
0917
0918 err = hashmap__add(specs_hash, target->spec_str, (void *)(long)*spec_id);
0919 if (err)
0920 return err;
0921
0922 man->free_spec_cnt--;
0923 } else {
0924
0925 if (man->next_free_spec_id >= bpf_map__max_entries(man->specs_map))
0926 return -E2BIG;
0927
0928 *spec_id = man->next_free_spec_id;
0929
0930
0931 err = hashmap__add(specs_hash, target->spec_str, (void *)(long)*spec_id);
0932 if (err)
0933 return err;
0934
0935 man->next_free_spec_id++;
0936 }
0937
0938
0939 link->spec_ids[link->spec_cnt] = *spec_id;
0940 link->spec_cnt++;
0941 *is_new = true;
0942 return 0;
0943 }
0944
0945 struct bpf_link *usdt_manager_attach_usdt(struct usdt_manager *man, const struct bpf_program *prog,
0946 pid_t pid, const char *path,
0947 const char *usdt_provider, const char *usdt_name,
0948 __u64 usdt_cookie)
0949 {
0950 int i, fd, err, spec_map_fd, ip_map_fd;
0951 LIBBPF_OPTS(bpf_uprobe_opts, opts);
0952 struct hashmap *specs_hash = NULL;
0953 struct bpf_link_usdt *link = NULL;
0954 struct usdt_target *targets = NULL;
0955 size_t target_cnt;
0956 Elf *elf;
0957
0958 spec_map_fd = bpf_map__fd(man->specs_map);
0959 ip_map_fd = bpf_map__fd(man->ip_to_spec_id_map);
0960
0961
0962 fd = open(path, O_RDONLY);
0963 if (fd < 0) {
0964 err = -errno;
0965 pr_warn("usdt: failed to open ELF binary '%s': %d\n", path, err);
0966 return libbpf_err_ptr(err);
0967 }
0968
0969 elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
0970 if (!elf) {
0971 err = -EBADF;
0972 pr_warn("usdt: failed to parse ELF binary '%s': %s\n", path, elf_errmsg(-1));
0973 goto err_out;
0974 }
0975
0976 err = sanity_check_usdt_elf(elf, path);
0977 if (err)
0978 goto err_out;
0979
0980
0981 if (pid < 0)
0982 pid = -1;
0983 else if (pid == 0)
0984 pid = getpid();
0985
0986
0987
0988
0989 err = collect_usdt_targets(man, elf, path, pid, usdt_provider, usdt_name,
0990 usdt_cookie, &targets, &target_cnt);
0991 if (err <= 0) {
0992 err = (err == 0) ? -ENOENT : err;
0993 goto err_out;
0994 }
0995
0996 specs_hash = hashmap__new(specs_hash_fn, specs_equal_fn, NULL);
0997 if (IS_ERR(specs_hash)) {
0998 err = PTR_ERR(specs_hash);
0999 goto err_out;
1000 }
1001
1002 link = calloc(1, sizeof(*link));
1003 if (!link) {
1004 err = -ENOMEM;
1005 goto err_out;
1006 }
1007
1008 link->usdt_man = man;
1009 link->link.detach = &bpf_link_usdt_detach;
1010 link->link.dealloc = &bpf_link_usdt_dealloc;
1011
1012 link->uprobes = calloc(target_cnt, sizeof(*link->uprobes));
1013 if (!link->uprobes) {
1014 err = -ENOMEM;
1015 goto err_out;
1016 }
1017
1018 for (i = 0; i < target_cnt; i++) {
1019 struct usdt_target *target = &targets[i];
1020 struct bpf_link *uprobe_link;
1021 bool is_new;
1022 int spec_id;
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032 err = allocate_spec_id(man, specs_hash, link, target, &spec_id, &is_new);
1033 if (err)
1034 goto err_out;
1035
1036 if (is_new && bpf_map_update_elem(spec_map_fd, &spec_id, &target->spec, BPF_ANY)) {
1037 err = -errno;
1038 pr_warn("usdt: failed to set USDT spec #%d for '%s:%s' in '%s': %d\n",
1039 spec_id, usdt_provider, usdt_name, path, err);
1040 goto err_out;
1041 }
1042 if (!man->has_bpf_cookie &&
1043 bpf_map_update_elem(ip_map_fd, &target->abs_ip, &spec_id, BPF_NOEXIST)) {
1044 err = -errno;
1045 if (err == -EEXIST) {
1046 pr_warn("usdt: IP collision detected for spec #%d for '%s:%s' in '%s'\n",
1047 spec_id, usdt_provider, usdt_name, path);
1048 } else {
1049 pr_warn("usdt: failed to map IP 0x%lx to spec #%d for '%s:%s' in '%s': %d\n",
1050 target->abs_ip, spec_id, usdt_provider, usdt_name,
1051 path, err);
1052 }
1053 goto err_out;
1054 }
1055
1056 opts.ref_ctr_offset = target->sema_off;
1057 opts.bpf_cookie = man->has_bpf_cookie ? spec_id : 0;
1058 uprobe_link = bpf_program__attach_uprobe_opts(prog, pid, path,
1059 target->rel_ip, &opts);
1060 err = libbpf_get_error(uprobe_link);
1061 if (err) {
1062 pr_warn("usdt: failed to attach uprobe #%d for '%s:%s' in '%s': %d\n",
1063 i, usdt_provider, usdt_name, path, err);
1064 goto err_out;
1065 }
1066
1067 link->uprobes[i].link = uprobe_link;
1068 link->uprobes[i].abs_ip = target->abs_ip;
1069 link->uprobe_cnt++;
1070 }
1071
1072 free(targets);
1073 hashmap__free(specs_hash);
1074 elf_end(elf);
1075 close(fd);
1076
1077 return &link->link;
1078
1079 err_out:
1080 if (link)
1081 bpf_link__destroy(&link->link);
1082 free(targets);
1083 hashmap__free(specs_hash);
1084 if (elf)
1085 elf_end(elf);
1086 close(fd);
1087 return libbpf_err_ptr(err);
1088 }
1089
1090
1091
1092
1093 static int parse_usdt_note(Elf *elf, const char *path, GElf_Nhdr *nhdr,
1094 const char *data, size_t name_off, size_t desc_off,
1095 struct usdt_note *note)
1096 {
1097 const char *provider, *name, *args;
1098 long addrs[3];
1099 size_t len;
1100
1101
1102 if (strncmp(data + name_off, USDT_NOTE_NAME, nhdr->n_namesz) != 0)
1103 return -EINVAL;
1104 if (nhdr->n_type != USDT_NOTE_TYPE)
1105 return -EINVAL;
1106
1107
1108 len = nhdr->n_descsz;
1109 data = data + desc_off;
1110
1111
1112 if (len < sizeof(addrs) + 3)
1113 return -EINVAL;
1114
1115
1116 memcpy(&addrs, data, sizeof(addrs));
1117
1118
1119 provider = data + sizeof(addrs);
1120
1121 name = (const char *)memchr(provider, '\0', data + len - provider);
1122 if (!name)
1123 return -EINVAL;
1124 name++;
1125 if (name >= data + len || *name == '\0')
1126 return -EINVAL;
1127
1128 args = memchr(name, '\0', data + len - name);
1129 if (!args)
1130 return -EINVAL;
1131 ++args;
1132 if (args >= data + len)
1133 return -EINVAL;
1134
1135 note->provider = provider;
1136 note->name = name;
1137 if (*args == '\0' || *args == ':')
1138 note->args = "";
1139 else
1140 note->args = args;
1141 note->loc_addr = addrs[0];
1142 note->base_addr = addrs[1];
1143 note->sema_addr = addrs[2];
1144
1145 return 0;
1146 }
1147
1148 static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec *arg);
1149
1150 static int parse_usdt_spec(struct usdt_spec *spec, const struct usdt_note *note, __u64 usdt_cookie)
1151 {
1152 const char *s;
1153 int len;
1154
1155 spec->usdt_cookie = usdt_cookie;
1156 spec->arg_cnt = 0;
1157
1158 s = note->args;
1159 while (s[0]) {
1160 if (spec->arg_cnt >= USDT_MAX_ARG_CNT) {
1161 pr_warn("usdt: too many USDT arguments (> %d) for '%s:%s' with args spec '%s'\n",
1162 USDT_MAX_ARG_CNT, note->provider, note->name, note->args);
1163 return -E2BIG;
1164 }
1165
1166 len = parse_usdt_arg(s, spec->arg_cnt, &spec->args[spec->arg_cnt]);
1167 if (len < 0)
1168 return len;
1169
1170 s += len;
1171 spec->arg_cnt++;
1172 }
1173
1174 return 0;
1175 }
1176
1177
1178
1179 #if defined(__x86_64__) || defined(__i386__)
1180
1181 static int calc_pt_regs_off(const char *reg_name)
1182 {
1183 static struct {
1184 const char *names[4];
1185 size_t pt_regs_off;
1186 } reg_map[] = {
1187 #ifdef __x86_64__
1188 #define reg_off(reg64, reg32) offsetof(struct pt_regs, reg64)
1189 #else
1190 #define reg_off(reg64, reg32) offsetof(struct pt_regs, reg32)
1191 #endif
1192 { {"rip", "eip", "", ""}, reg_off(rip, eip) },
1193 { {"rax", "eax", "ax", "al"}, reg_off(rax, eax) },
1194 { {"rbx", "ebx", "bx", "bl"}, reg_off(rbx, ebx) },
1195 { {"rcx", "ecx", "cx", "cl"}, reg_off(rcx, ecx) },
1196 { {"rdx", "edx", "dx", "dl"}, reg_off(rdx, edx) },
1197 { {"rsi", "esi", "si", "sil"}, reg_off(rsi, esi) },
1198 { {"rdi", "edi", "di", "dil"}, reg_off(rdi, edi) },
1199 { {"rbp", "ebp", "bp", "bpl"}, reg_off(rbp, ebp) },
1200 { {"rsp", "esp", "sp", "spl"}, reg_off(rsp, esp) },
1201 #undef reg_off
1202 #ifdef __x86_64__
1203 { {"r8", "r8d", "r8w", "r8b"}, offsetof(struct pt_regs, r8) },
1204 { {"r9", "r9d", "r9w", "r9b"}, offsetof(struct pt_regs, r9) },
1205 { {"r10", "r10d", "r10w", "r10b"}, offsetof(struct pt_regs, r10) },
1206 { {"r11", "r11d", "r11w", "r11b"}, offsetof(struct pt_regs, r11) },
1207 { {"r12", "r12d", "r12w", "r12b"}, offsetof(struct pt_regs, r12) },
1208 { {"r13", "r13d", "r13w", "r13b"}, offsetof(struct pt_regs, r13) },
1209 { {"r14", "r14d", "r14w", "r14b"}, offsetof(struct pt_regs, r14) },
1210 { {"r15", "r15d", "r15w", "r15b"}, offsetof(struct pt_regs, r15) },
1211 #endif
1212 };
1213 int i, j;
1214
1215 for (i = 0; i < ARRAY_SIZE(reg_map); i++) {
1216 for (j = 0; j < ARRAY_SIZE(reg_map[i].names); j++) {
1217 if (strcmp(reg_name, reg_map[i].names[j]) == 0)
1218 return reg_map[i].pt_regs_off;
1219 }
1220 }
1221
1222 pr_warn("usdt: unrecognized register '%s'\n", reg_name);
1223 return -ENOENT;
1224 }
1225
1226 static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec *arg)
1227 {
1228 char *reg_name = NULL;
1229 int arg_sz, len, reg_off;
1230 long off;
1231
1232 if (sscanf(arg_str, " %d @ %ld ( %%%m[^)] ) %n", &arg_sz, &off, ®_name, &len) == 3) {
1233
1234 arg->arg_type = USDT_ARG_REG_DEREF;
1235 arg->val_off = off;
1236 reg_off = calc_pt_regs_off(reg_name);
1237 free(reg_name);
1238 if (reg_off < 0)
1239 return reg_off;
1240 arg->reg_off = reg_off;
1241 } else if (sscanf(arg_str, " %d @ %%%ms %n", &arg_sz, ®_name, &len) == 2) {
1242
1243 arg->arg_type = USDT_ARG_REG;
1244 arg->val_off = 0;
1245
1246 reg_off = calc_pt_regs_off(reg_name);
1247 free(reg_name);
1248 if (reg_off < 0)
1249 return reg_off;
1250 arg->reg_off = reg_off;
1251 } else if (sscanf(arg_str, " %d @ $%ld %n", &arg_sz, &off, &len) == 2) {
1252
1253 arg->arg_type = USDT_ARG_CONST;
1254 arg->val_off = off;
1255 arg->reg_off = 0;
1256 } else {
1257 pr_warn("usdt: unrecognized arg #%d spec '%s'\n", arg_num, arg_str);
1258 return -EINVAL;
1259 }
1260
1261 arg->arg_signed = arg_sz < 0;
1262 if (arg_sz < 0)
1263 arg_sz = -arg_sz;
1264
1265 switch (arg_sz) {
1266 case 1: case 2: case 4: case 8:
1267 arg->arg_bitshift = 64 - arg_sz * 8;
1268 break;
1269 default:
1270 pr_warn("usdt: unsupported arg #%d (spec '%s') size: %d\n",
1271 arg_num, arg_str, arg_sz);
1272 return -EINVAL;
1273 }
1274
1275 return len;
1276 }
1277
1278 #elif defined(__s390x__)
1279
1280
1281
1282 static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec *arg)
1283 {
1284 unsigned int reg;
1285 int arg_sz, len;
1286 long off;
1287
1288 if (sscanf(arg_str, " %d @ %ld ( %%r%u ) %n", &arg_sz, &off, ®, &len) == 3) {
1289
1290 arg->arg_type = USDT_ARG_REG_DEREF;
1291 arg->val_off = off;
1292 if (reg > 15) {
1293 pr_warn("usdt: unrecognized register '%%r%u'\n", reg);
1294 return -EINVAL;
1295 }
1296 arg->reg_off = offsetof(user_pt_regs, gprs[reg]);
1297 } else if (sscanf(arg_str, " %d @ %%r%u %n", &arg_sz, ®, &len) == 2) {
1298
1299 arg->arg_type = USDT_ARG_REG;
1300 arg->val_off = 0;
1301 if (reg > 15) {
1302 pr_warn("usdt: unrecognized register '%%r%u'\n", reg);
1303 return -EINVAL;
1304 }
1305 arg->reg_off = offsetof(user_pt_regs, gprs[reg]);
1306 } else if (sscanf(arg_str, " %d @ %ld %n", &arg_sz, &off, &len) == 2) {
1307
1308 arg->arg_type = USDT_ARG_CONST;
1309 arg->val_off = off;
1310 arg->reg_off = 0;
1311 } else {
1312 pr_warn("usdt: unrecognized arg #%d spec '%s'\n", arg_num, arg_str);
1313 return -EINVAL;
1314 }
1315
1316 arg->arg_signed = arg_sz < 0;
1317 if (arg_sz < 0)
1318 arg_sz = -arg_sz;
1319
1320 switch (arg_sz) {
1321 case 1: case 2: case 4: case 8:
1322 arg->arg_bitshift = 64 - arg_sz * 8;
1323 break;
1324 default:
1325 pr_warn("usdt: unsupported arg #%d (spec '%s') size: %d\n",
1326 arg_num, arg_str, arg_sz);
1327 return -EINVAL;
1328 }
1329
1330 return len;
1331 }
1332
1333 #elif defined(__aarch64__)
1334
1335 static int calc_pt_regs_off(const char *reg_name)
1336 {
1337 int reg_num;
1338
1339 if (sscanf(reg_name, "x%d", ®_num) == 1) {
1340 if (reg_num >= 0 && reg_num < 31)
1341 return offsetof(struct user_pt_regs, regs[reg_num]);
1342 } else if (strcmp(reg_name, "sp") == 0) {
1343 return offsetof(struct user_pt_regs, sp);
1344 }
1345 pr_warn("usdt: unrecognized register '%s'\n", reg_name);
1346 return -ENOENT;
1347 }
1348
1349 static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec *arg)
1350 {
1351 char *reg_name = NULL;
1352 int arg_sz, len, reg_off;
1353 long off;
1354
1355 if (sscanf(arg_str, " %d @ \[ %m[a-z0-9], %ld ] %n", &arg_sz, ®_name, &off, &len) == 3) {
1356
1357 arg->arg_type = USDT_ARG_REG_DEREF;
1358 arg->val_off = off;
1359 reg_off = calc_pt_regs_off(reg_name);
1360 free(reg_name);
1361 if (reg_off < 0)
1362 return reg_off;
1363 arg->reg_off = reg_off;
1364 } else if (sscanf(arg_str, " %d @ \[ %m[a-z0-9] ] %n", &arg_sz, ®_name, &len) == 2) {
1365
1366 arg->arg_type = USDT_ARG_REG_DEREF;
1367 arg->val_off = 0;
1368 reg_off = calc_pt_regs_off(reg_name);
1369 free(reg_name);
1370 if (reg_off < 0)
1371 return reg_off;
1372 arg->reg_off = reg_off;
1373 } else if (sscanf(arg_str, " %d @ %ld %n", &arg_sz, &off, &len) == 2) {
1374
1375 arg->arg_type = USDT_ARG_CONST;
1376 arg->val_off = off;
1377 arg->reg_off = 0;
1378 } else if (sscanf(arg_str, " %d @ %m[a-z0-9] %n", &arg_sz, ®_name, &len) == 2) {
1379
1380 arg->arg_type = USDT_ARG_REG;
1381 arg->val_off = 0;
1382 reg_off = calc_pt_regs_off(reg_name);
1383 free(reg_name);
1384 if (reg_off < 0)
1385 return reg_off;
1386 arg->reg_off = reg_off;
1387 } else {
1388 pr_warn("usdt: unrecognized arg #%d spec '%s'\n", arg_num, arg_str);
1389 return -EINVAL;
1390 }
1391
1392 arg->arg_signed = arg_sz < 0;
1393 if (arg_sz < 0)
1394 arg_sz = -arg_sz;
1395
1396 switch (arg_sz) {
1397 case 1: case 2: case 4: case 8:
1398 arg->arg_bitshift = 64 - arg_sz * 8;
1399 break;
1400 default:
1401 pr_warn("usdt: unsupported arg #%d (spec '%s') size: %d\n",
1402 arg_num, arg_str, arg_sz);
1403 return -EINVAL;
1404 }
1405
1406 return len;
1407 }
1408
1409 #elif defined(__riscv)
1410
1411 static int calc_pt_regs_off(const char *reg_name)
1412 {
1413 static struct {
1414 const char *name;
1415 size_t pt_regs_off;
1416 } reg_map[] = {
1417 { "ra", offsetof(struct user_regs_struct, ra) },
1418 { "sp", offsetof(struct user_regs_struct, sp) },
1419 { "gp", offsetof(struct user_regs_struct, gp) },
1420 { "tp", offsetof(struct user_regs_struct, tp) },
1421 { "a0", offsetof(struct user_regs_struct, a0) },
1422 { "a1", offsetof(struct user_regs_struct, a1) },
1423 { "a2", offsetof(struct user_regs_struct, a2) },
1424 { "a3", offsetof(struct user_regs_struct, a3) },
1425 { "a4", offsetof(struct user_regs_struct, a4) },
1426 { "a5", offsetof(struct user_regs_struct, a5) },
1427 { "a6", offsetof(struct user_regs_struct, a6) },
1428 { "a7", offsetof(struct user_regs_struct, a7) },
1429 { "s0", offsetof(struct user_regs_struct, s0) },
1430 { "s1", offsetof(struct user_regs_struct, s1) },
1431 { "s2", offsetof(struct user_regs_struct, s2) },
1432 { "s3", offsetof(struct user_regs_struct, s3) },
1433 { "s4", offsetof(struct user_regs_struct, s4) },
1434 { "s5", offsetof(struct user_regs_struct, s5) },
1435 { "s6", offsetof(struct user_regs_struct, s6) },
1436 { "s7", offsetof(struct user_regs_struct, s7) },
1437 { "s8", offsetof(struct user_regs_struct, rv_s8) },
1438 { "s9", offsetof(struct user_regs_struct, s9) },
1439 { "s10", offsetof(struct user_regs_struct, s10) },
1440 { "s11", offsetof(struct user_regs_struct, s11) },
1441 { "t0", offsetof(struct user_regs_struct, t0) },
1442 { "t1", offsetof(struct user_regs_struct, t1) },
1443 { "t2", offsetof(struct user_regs_struct, t2) },
1444 { "t3", offsetof(struct user_regs_struct, t3) },
1445 { "t4", offsetof(struct user_regs_struct, t4) },
1446 { "t5", offsetof(struct user_regs_struct, t5) },
1447 { "t6", offsetof(struct user_regs_struct, t6) },
1448 };
1449 int i;
1450
1451 for (i = 0; i < ARRAY_SIZE(reg_map); i++) {
1452 if (strcmp(reg_name, reg_map[i].name) == 0)
1453 return reg_map[i].pt_regs_off;
1454 }
1455
1456 pr_warn("usdt: unrecognized register '%s'\n", reg_name);
1457 return -ENOENT;
1458 }
1459
1460 static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec *arg)
1461 {
1462 char *reg_name = NULL;
1463 int arg_sz, len, reg_off;
1464 long off;
1465
1466 if (sscanf(arg_str, " %d @ %ld ( %m[a-z0-9] ) %n", &arg_sz, &off, ®_name, &len) == 3) {
1467
1468 arg->arg_type = USDT_ARG_REG_DEREF;
1469 arg->val_off = off;
1470 reg_off = calc_pt_regs_off(reg_name);
1471 free(reg_name);
1472 if (reg_off < 0)
1473 return reg_off;
1474 arg->reg_off = reg_off;
1475 } else if (sscanf(arg_str, " %d @ %ld %n", &arg_sz, &off, &len) == 2) {
1476
1477 arg->arg_type = USDT_ARG_CONST;
1478 arg->val_off = off;
1479 arg->reg_off = 0;
1480 } else if (sscanf(arg_str, " %d @ %m[a-z0-9] %n", &arg_sz, ®_name, &len) == 2) {
1481
1482 arg->arg_type = USDT_ARG_REG;
1483 arg->val_off = 0;
1484 reg_off = calc_pt_regs_off(reg_name);
1485 free(reg_name);
1486 if (reg_off < 0)
1487 return reg_off;
1488 arg->reg_off = reg_off;
1489 } else {
1490 pr_warn("usdt: unrecognized arg #%d spec '%s'\n", arg_num, arg_str);
1491 return -EINVAL;
1492 }
1493
1494 arg->arg_signed = arg_sz < 0;
1495 if (arg_sz < 0)
1496 arg_sz = -arg_sz;
1497
1498 switch (arg_sz) {
1499 case 1: case 2: case 4: case 8:
1500 arg->arg_bitshift = 64 - arg_sz * 8;
1501 break;
1502 default:
1503 pr_warn("usdt: unsupported arg #%d (spec '%s') size: %d\n",
1504 arg_num, arg_str, arg_sz);
1505 return -EINVAL;
1506 }
1507
1508 return len;
1509 }
1510
1511 #else
1512
1513 static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec *arg)
1514 {
1515 pr_warn("usdt: libbpf doesn't support USDTs on current architecture\n");
1516 return -ENOTSUP;
1517 }
1518
1519 #endif