📄 fbt.c
字号:
} } /* * And mark the locals used in the restore. */ rs1 = FBT_FMT3_RS1(restore); FBT_REG_MARKLOCAL(locals, rs1); if (!FBT_FMT3_ISIMM(restore)) { uint32_t rs2 = FBT_FMT3_RS2(restore); FBT_REG_MARKLOCAL(locals, rs2); } if (FBT_FMT3_OP(cti) == FBT_OP_JMPL) { uint32_t rs1 = FBT_FMT3_RS1(cti); if (FBT_REG_ISVOLATILE(rs1)) { FBT_REG_ALLOCLOCAL(local, locals); FBT_FMT3_RS1_SET(cti, local); *tinstr++ = FBT_MOV(rs1, local); } if (!FBT_FMT3_ISIMM(cti)) { uint32_t rs2 = FBT_FMT3_RS2(cti); if (FBT_REG_ISVOLATILE(rs2)) { FBT_REG_ALLOCLOCAL(local, locals); FBT_FMT3_RS2_SET(cti, local); *tinstr++ = FBT_MOV(rs2, local); } } } rs1 = FBT_FMT3_RS1(restore); if (FBT_REG_ISVOLATILE(rs1)) { FBT_REG_ALLOCLOCAL(local, locals); FBT_FMT3_RS1_SET(restore, local); *tinstr++ = FBT_MOV(rs1, local); } if (!FBT_FMT3_ISIMM(restore)) { uint32_t rs2 = FBT_FMT3_RS2(restore); if (FBT_REG_ISVOLATILE(rs2)) { FBT_REG_ALLOCLOCAL(local, locals); FBT_FMT3_RS2_SET(restore, local); *tinstr++ = FBT_MOV(rs2, local); } } if (id > (uint32_t)FBT_SIMM13_MAX) { *tinstr++ = FBT_SETHI(id, FBT_REG_O0); *tinstr++ = FBT_ORLO(FBT_REG_O0, id, FBT_REG_O0); } else { *tinstr++ = FBT_ORSIMM13(FBT_REG_G0, id, FBT_REG_O0); } if (offset > (uint32_t)FBT_SIMM13_MAX) { *tinstr++ = FBT_SETHI(offset, FBT_REG_O1); *tinstr++ = FBT_ORLO(FBT_REG_O1, offset, FBT_REG_O1); } else { *tinstr++ = FBT_ORSIMM13(FBT_REG_G0, offset, FBT_REG_O1); } *tinstr = FBT_CALL((uintptr_t)tinstr - base + va, dtrace_probe); tinstr++; if (FBT_FMT3_RD(restore) == FBT_REG_O0) { /* * If the destination register of the restore is %o0, we * need to perform the implied calculation to derive the * return value. */ uint32_t add = (restore & ~FBT_FMT3_OP_MASK) | FBT_OP_ADD; add &= ~FBT_FMT3_RD_MASK; *tinstr++ = add | (FBT_REG_O2 << FBT_FMT3_RD_SHIFT); } else { *tinstr++ = FBT_MOV(FBT_REG_I0, FBT_REG_O2); } /* * If the control transfer instruction is %pc-relative (i.e. a * call), we need to reset it appropriately. */ if (FBT_FMT1_OP(cti) == FBT_OP_CALL) { dest = (uintptr_t)instr + (FBT_FMT1_DISP30(cti) << 2); *tinstr = FBT_CALL((uintptr_t)tinstr - base + va, dest); tinstr++; } else { *tinstr++ = cti; } *tinstr++ = restore; tramp->fbtt_va += (uintptr_t)tinstr - tramp->fbtt_next; tramp->fbtt_next = (uintptr_t)tinstr; return (FBT_BAA(instr, va));}static uint32_tfbt_patch_retl(uint32_t *instr, uint32_t *funcbase, uint32_t *funclim, int offset, uint32_t id, fbt_trampoline_t *tramp){ uint32_t *tinstr = (uint32_t *)tramp->fbtt_next; uintptr_t va = tramp->fbtt_va; uintptr_t base = tramp->fbtt_next; uint32_t cti = *instr, dest; int annul = 0; FBT_COUNTER(id, fbt_retl); if (tramp->fbtt_next + FBT_RETLENT_MAXSIZE > tramp->fbtt_limit) { /* * There isn't sufficient room for this entry; return failure. */ return (FBT_ILLTRAP); } if (offset == sizeof (uint32_t) && fbt_canpatch_retl(instr - 1, 0)) { *tinstr++ = *instr; annul = 1; FBT_COUNTER(id, fbt_retl_twoinstr); } else { if (FBT_FMT3_OP(cti) == FBT_OP_JMPL && FBT_FMT3_RD(cti) != FBT_REG_O7 && FBT_FMT3_RS1(cti) != FBT_REG_O7) { annul = 1; *tinstr++ = *(instr + 1); } } *tinstr++ = FBT_SAVEIMM(FBT_REG_O6, -SA(MINFRAME), FBT_REG_O6); if (FBT_FMT3_OP(cti) == FBT_OP_JMPL) { uint32_t rs1, rs2, o2i = FBT_REG_I0 - FBT_REG_O0; /* * If we have a jmpl and it's in terms of output registers, we * need to rewrite it to be in terms of the corresponding input * registers. If it's in terms of the globals, we'll rewrite * it to be in terms of locals. */ rs1 = FBT_FMT3_RS1(cti); if (FBT_REG_ISOUTPUT(rs1)) rs1 += o2i; if (FBT_REG_ISGLOBAL(rs1)) { *tinstr++ = FBT_MOV(rs1, FBT_REG_L0); rs1 = FBT_REG_L0; } FBT_FMT3_RS1_SET(cti, rs1); if (!FBT_FMT3_ISIMM(cti)) { rs2 = FBT_FMT3_RS2(cti); if (FBT_REG_ISOUTPUT(rs2)) rs2 += o2i; if (FBT_REG_ISGLOBAL(rs2)) { *tinstr++ = FBT_MOV(rs2, FBT_REG_L1); rs2 = FBT_REG_L1; } FBT_FMT3_RS2_SET(cti, rs2); } /* * Now we need to check the rd and source register for the jmpl; * If neither rd nor the source register is %o7, then we might * have a jmp that is actually part of a jump table. We need * to generate the code to compare it to the base and limit of * the function. */ if (FBT_FMT3_RD(cti) != FBT_REG_O7 && rs1 != FBT_REG_I7) { uintptr_t base = (uintptr_t)funcbase; uintptr_t limit = (uintptr_t)funclim; FBT_COUNTER(id, fbt_retl_jmptab); if (FBT_FMT3_ISIMM(cti)) { *tinstr++ = FBT_ADDSIMM13(rs1, FBT_FMT3_SIMM13(cti), FBT_REG_L2); } else { *tinstr++ = FBT_ADD(rs1, rs2, FBT_REG_L2); } *tinstr++ = FBT_SETHI(base, FBT_REG_L3); *tinstr++ = FBT_ORLO(FBT_REG_L3, base, FBT_REG_L3); *tinstr++ = FBT_CMP(FBT_REG_L2, FBT_REG_L3); *tinstr++ = FBT_BL(0, 8 * sizeof (uint32_t)); *tinstr++ = FBT_SETHI(limit, FBT_REG_L3); *tinstr++ = FBT_ORLO(FBT_REG_L3, limit, FBT_REG_L3); *tinstr++ = FBT_CMP(FBT_REG_L2, FBT_REG_L3); *tinstr++ = FBT_BGE(0, 4 * sizeof (uint32_t)); *tinstr++ = FBT_SETHI(0, FBT_REG_G0); *tinstr++ = cti; *tinstr++ = FBT_RESTORE(FBT_REG_G0, FBT_REG_G0, FBT_REG_G0); } } if (id > (uint32_t)FBT_SIMM13_MAX) { *tinstr++ = FBT_SETHI(id, FBT_REG_O0); *tinstr++ = FBT_ORLO(FBT_REG_O0, id, FBT_REG_O0); } else { *tinstr++ = FBT_ORSIMM13(FBT_REG_G0, id, FBT_REG_O0); } if (offset > (uint32_t)FBT_SIMM13_MAX) { *tinstr++ = FBT_SETHI(offset, FBT_REG_O1); *tinstr++ = FBT_ORLO(FBT_REG_O1, offset, FBT_REG_O1); } else { *tinstr++ = FBT_ORSIMM13(FBT_REG_G0, offset, FBT_REG_O1); } *tinstr = FBT_CALL((uintptr_t)tinstr - base + va, dtrace_probe); tinstr++; *tinstr++ = FBT_MOV(FBT_REG_I0, FBT_REG_O2); /* * If the control transfer instruction is %pc-relative (i.e. a * call), we need to reset it appropriately. */ if (FBT_FMT1_OP(cti) == FBT_OP_CALL) { FBT_COUNTER(id, fbt_retl_tailcall); dest = (uintptr_t)instr + (FBT_FMT1_DISP30(cti) << 2); *tinstr = FBT_CALL((uintptr_t)tinstr - base + va, dest); tinstr++; annul = 1; } else { if (FBT_FMT3_OP(cti) == FBT_OP_JMPL) { *tinstr++ = cti; if (FBT_FMT3_RD(cti) == FBT_REG_O7) { FBT_COUNTER(id, fbt_retl_tailjmpl); annul = 1; } } else { *tinstr++ = FBT_RET; } } *tinstr++ = FBT_RESTORE(FBT_REG_G0, FBT_REG_G0, FBT_REG_G0); tramp->fbtt_va += (uintptr_t)tinstr - tramp->fbtt_next; tramp->fbtt_next = (uintptr_t)tinstr; return (annul ? FBT_BAA(instr, va) : FBT_BA(instr, va));}/*ARGSUSED*/static voidfbt_provide_module(void *arg, struct modctl *ctl){ struct module *mp = ctl->mod_mp; char *modname = ctl->mod_modname; char *str = mp->strings; int nsyms = mp->nsyms; Shdr *symhdr = mp->symhdr; size_t symsize; char *name; int i; fbt_probe_t *fbt, *retfbt; fbt_trampoline_t tramp; uintptr_t offset; int primary = 0; ctf_file_t *fp = NULL; int error; int estimate = 1; uint32_t faketramp[50]; size_t fbt_size = 0; /* * Employees of dtrace and their families are ineligible. Void * where prohibited. */ if (strcmp(modname, "dtrace") == 0) return; if (ctl->mod_requisites != NULL) { struct modctl_list *list; list = (struct modctl_list *)ctl->mod_requisites; for (; list != NULL; list = list->modl_next) { if (strcmp(list->modl_modp->mod_modname, "dtrace") == 0) return; } } /* * KMDB is ineligible for instrumentation -- it may execute in * any context, including probe context. */ if (strcmp(modname, "kmdbmod") == 0) return; if (str == NULL || symhdr == NULL || symhdr->sh_addr == NULL) { /* * If this module doesn't (yet) have its string or symbol * table allocated, clear out. */ return; } symsize = symhdr->sh_entsize; if (mp->fbt_nentries) { /* * This module has some FBT entries allocated; we're afraid * to screw with it. */ return; } if (mp->fbt_tab != NULL) estimate = 0; /* * This is a hack for unix/genunix/krtld. */ primary = vmem_contains(heap_arena, (void *)ctl, sizeof (struct modctl)) == 0; kobj_textwin_alloc(mp); /* * Open the CTF data for the module. We'll use this to determine the * functions that can be instrumented. Note that this call can fail, * in which case we'll use heuristics to determine the functions that * can be instrumented. (But in particular, leaf functions will not be * instrumented.) */ fp = ctf_modopen(mp, &error);forreal: if (!estimate) { tramp.fbtt_next = (uintptr_t)fbt_trampoline_map((uintptr_t)mp->fbt_tab, mp->fbt_size); tramp.fbtt_limit = tramp.fbtt_next + mp->fbt_size; tramp.fbtt_va = (uintptr_t)mp->fbt_tab; } for (i = 1; i < nsyms; i++) { ctf_funcinfo_t f; uint32_t *instr, *base, *limit; Sym *sym = (Sym *)(symhdr->sh_addr + i * symsize); int have_ctf = 0, is_leaf = 0, nargs, cti = 0; int (*canpatch)(uint32_t *, int); uint32_t (*patch)(uint32_t *, uint32_t *, uint32_t *, int, uint32_t, fbt_trampoline_t *); if (ELF_ST_TYPE(sym->st_info) != STT_FUNC) continue; /* * Weak symbols are not candidates. This could be made to * work (where weak functions and their underlying function * appear as two disjoint probes), but it's not simple. */ if (ELF_ST_BIND(sym->st_info) == STB_WEAK) continue; name = str + sym->st_name; if (strstr(name, "dtrace_") == name && strstr(name, "dtrace_safe_") != name) { /* * Anything beginning with "dtrace_" may be called * from probe context unless it explitly indicates * that it won't be called from probe context by * using the prefix "dtrace_safe_". */ continue; } if (strstr(name, "kdi_") == name) { /* * Anything beginning with "kdi_" is a part of the * kernel debugger interface and may be called in * arbitrary context -- including probe context. */ continue; } if (strstr(name, "__relocatable") != NULL) { /* * Anything with the string "__relocatable" anywhere * in the function name is considered to be a function * that may be manually relocated before execution. * Because FBT uses a PC-relative technique for * instrumentation, these functions cannot safely * be instrumented by us. */ continue; } if (strstr(name, "ip_ocsum") == name) { /* * The ip_ocsum_* family of routines are all ABI * violators. (They expect incoming arguments in the * globals!) Break the ABI? No soup for you! */ continue; } /* * We want to scan the function for one (and only one) save. * Any more indicates that something fancy is going on. */ base = (uint32_t *)sym->st_value; limit = (uint32_t *)(sym->st_value + sym->st_size); /* * We don't want to interpose on the module stubs. */ if (base >= (uint32_t *)stubs_base && base <= (uint32_t *)stubs_end) continue; /* * We can't safely trace a zero-length function... */ if (base == limit) continue; /* * Due to 4524008, _init and _fini may have a bloated st_size. * While this bug was fixed quite some time ago, old drivers * may be lurking. We need to develop a better solution to * this problem, such that correct _init and _fini functions * (the vast majority) may be correctly traced. One solution * may be to scan through the entire symbol table to see if * any symbol overlaps with _init. If none does, set a bit in * the module structure that this module has correct _init and * _fini sizes. This will cause some pain the first time a * module is scanned, but at least it would be O(N) instead of * O(N log N)... */ if (strcmp(name, "_init") == 0) continue; if (strcmp(name, "_fini") == 0) continue; instr = base; /* * While we try hard to only trace safe functions (that is, * functions at TL=0), one unsafe function manages to otherwise * appear safe: prom_trap(). We could discover prom_trap() * if we added an additional rule: in order to trace a * function, we must either (a) discover a restore or (b) * determine that the function does not have any unlinked * control transfers to another function (i.e., the function * never returns). Unfortunately, as of this writing, one * legitimate function (resume_from_zombie()) transfers * control to a different function (_resume_from_idle()) * without executing a restore. Barring a rule to figure out * that resume_from_zombie() is safe while prom_trap() is not, * we resort to hard-coding prom_trap() here. */ if (strcmp(name, "prom_trap") == 0) continue; if (fp != NULL && ctf_func_info(fp, i, &f) != CTF_ERR) { nargs = f.ctc_argc; have_ctf = 1; } else { nargs = 32; } /* * If the first instruction of the function is a branch and * it's not a branch-always-not-annulled, we're going to refuse * to patch it. */ if ((*instr & FBT_OP_MASK) == FBT_OP0 && (*instr & FBT_FMT2_OP2_MASK) != FBT_FMT2_OP2_SETHI) { if (!FBT_IS_BA(*instr)) { if (have_ctf) { cmn_err(CE_NOTE, "cannot instrument %s:" " begins with non-ba CTI", name); } continue; } } while (!FBT_IS_SAVE(*instr)) { /* * Before we assume that this is a leaf routine, check * forward in the basic block for a save. */ int op = *instr & FBT_OP_MASK; int op2 = *instr & FBT_FMT2_OP2_MASK; if (op == FBT_OP0 && op2 != FBT_FMT2_OP2_SETHI) { /* * This is a CTI. If we see a subsequent * save, we will refuse to process this * routine unless both of the following are * true: * * (a) The branch is not annulled * * (b) The subsequent save is in the delay * slot of the branch */ if ((*instr & FBT_ANNUL) || !FBT_IS_SAVE(*(instr + 1))) { cti = 1; } else { instr++; break; } } if (op == FBT_OP1) cti = 1; if (++instr == limit) break; } if (instr < limit && cti) { /* * If we found a CTI before the save, we need to not * do anything. But if we have CTF information, this * is weird enough that it merits a message. */ if (!have_ctf) continue;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -