📄 fbt.c
字号:
/* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only. * See the file usr/src/LICENSING.NOTICE in this distribution or * http://www.opensolaris.org/license/ for details. */#pragma ident "@(#)fbt.c 1.8 04/12/18 SMI"#include <sys/errno.h>#include <sys/stat.h>#include <sys/modctl.h>#include <sys/conf.h>#include <sys/systm.h>#include <sys/ddi.h>#include <sys/sunddi.h>#include <sys/cpuvar.h>#include <sys/kmem.h>#include <sys/strsubr.h>#include <sys/dtrace.h>#include <sys/kobj.h>#include <sys/modctl.h>#include <sys/atomic.h>#include <vm/seg_kmem.h>#include <sys/stack.h>#include <sys/ctf_api.h>#include <sys/sysmacros.h>static dev_info_t *fbt_devi;static dtrace_provider_id_t fbt_id;static uintptr_t fbt_trampoline;static caddr_t fbt_trampoline_window;static size_t fbt_trampoline_size;static int fbt_verbose = 0;/* * Various interesting bean counters. */static int fbt_entry;static int fbt_ret;static int fbt_retl;static int fbt_retl_jmptab;static int fbt_retl_twoinstr;static int fbt_retl_tailcall;static int fbt_retl_tailjmpl;static int fbt_leaf_functions;extern char stubs_base[];extern char stubs_end[];#define FBT_REG_G0 0#define FBT_REG_G1 1#define FBT_REG_O0 8#define FBT_REG_O1 9#define FBT_REG_O2 10#define FBT_REG_O3 11#define FBT_REG_O4 12#define FBT_REG_O5 13#define FBT_REG_O6 14#define FBT_REG_O7 15#define FBT_REG_I0 24#define FBT_REG_I1 25#define FBT_REG_I2 26#define FBT_REG_I3 27#define FBT_REG_I4 28#define FBT_REG_I7 31#define FBT_REG_L0 16#define FBT_REG_L1 17#define FBT_REG_L2 18#define FBT_REG_L3 19#define FBT_REG_ISGLOBAL(r) ((r) < 8)#define FBT_REG_ISOUTPUT(r) ((r) >= 8 && (r) < 16)#define FBT_REG_ISLOCAL(r) ((r) >= 16 && (r) < 24)#define FBT_REG_ISVOLATILE(r) \ ((FBT_REG_ISGLOBAL(r) || FBT_REG_ISOUTPUT(r)) && (r) != FBT_REG_G0)#define FBT_REG_NLOCALS 8#define FBT_REG_MARKLOCAL(locals, r) \ if (FBT_REG_ISLOCAL(r)) \ (locals)[(r) - FBT_REG_L0] = 1;#define FBT_REG_INITLOCALS(local, locals) \ for ((local) = 0; (local) < FBT_REG_NLOCALS; (local)++) \ (locals)[(local)] = 0; \ (local) = FBT_REG_L0#define FBT_REG_ALLOCLOCAL(local, locals) \ while ((locals)[(local) - FBT_REG_L0]) \ (local)++; \ (locals)[(local) - FBT_REG_L0] = 1;#define FBT_OP_MASK 0xc0000000#define FBT_OP_SHIFT 30#define FBT_OP(val) ((val) & FBT_FMT1_MASK)#define FBT_SIMM13_MASK 0x1fff#define FBT_SIMM13_MAX ((int32_t)0xfff)#define FBT_IMM22_MASK 0x3fffff#define FBT_IMM22_SHIFT 10#define FBT_IMM10_MASK 0x3ff#define FBT_DISP30_MASK 0x3fffffff#define FBT_DISP30(from, to) \ (((uintptr_t)(to) - (uintptr_t)(from) >> 2) & FBT_DISP30_MASK)#define FBT_DISP22_MASK 0x3fffff#define FBT_DISP22(from, to) \ (((uintptr_t)(to) - (uintptr_t)(from) >> 2) & FBT_DISP22_MASK)#define FBT_OP0 (((uint32_t)0) << FBT_OP_SHIFT)#define FBT_OP1 (((uint32_t)1) << FBT_OP_SHIFT)#define FBT_OP2 (((uint32_t)2) << FBT_OP_SHIFT)#define FBT_ILLTRAP 0#define FBT_ANNUL_SHIFT 29#define FBT_ANNUL (1 << FBT_ANNUL_SHIFT)#define FBT_FMT3_OP3_SHIFT 19#define FBT_FMT3_OP_MASK 0xc1f80000#define FBT_FMT3_OP(val) ((val) & FBT_FMT3_OP_MASK)#define FBT_FMT3_RD_SHIFT 25#define FBT_FMT3_RD_MASK (0x1f << FBT_FMT3_RD_SHIFT)#define FBT_FMT3_RD(val) \ (((val) & FBT_FMT3_RD_MASK) >> FBT_FMT3_RD_SHIFT)#define FBT_FMT3_RS1_SHIFT 14#define FBT_FMT3_RS1_MASK (0x1f << FBT_FMT3_RS1_SHIFT)#define FBT_FMT3_RS1(val) \ (((val) & FBT_FMT3_RS1_MASK) >> FBT_FMT3_RS1_SHIFT)#define FBT_FMT3_RS1_SET(val, rs1) \ (val) = ((val) & ~FBT_FMT3_RS1_MASK) | ((rs1) << FBT_FMT3_RS1_SHIFT)#define FBT_FMT3_RS2_SHIFT 0#define FBT_FMT3_RS2_MASK (0x1f << FBT_FMT3_RS2_SHIFT)#define FBT_FMT3_RS2(val) \ (((val) & FBT_FMT3_RS2_MASK) >> FBT_FMT3_RS2_SHIFT)#define FBT_FMT3_RS2_SET(val, rs2) \ (val) = ((val) & ~FBT_FMT3_RS2_MASK) | ((rs2) << FBT_FMT3_RS2_SHIFT)#define FBT_FMT3_IMM_SHIFT 13#define FBT_FMT3_IMM (1 << FBT_FMT3_IMM_SHIFT)#define FBT_FMT3_SIMM13_MASK FBT_SIMM13_MASK#define FBT_FMT3_ISIMM(val) ((val) & FBT_FMT3_IMM)#define FBT_FMT3_SIMM13(val) ((val) & FBT_FMT3_SIMM13_MASK)#define FBT_FMT2_OP2_SHIFT 22#define FBT_FMT2_OP2_MASK (0x7 << FBT_FMT2_OP2_SHIFT)#define FBT_FMT2_RD_SHIFT 25#define FBT_FMT1_OP(val) ((val) & FBT_OP_MASK)#define FBT_FMT1_DISP30(val) ((val) & FBT_DISP30_MASK)#define FBT_FMT2_OP2_BCC (0x02 << FBT_FMT2_OP2_SHIFT)#define FBT_FMT2_OP2_SETHI (0x04 << FBT_FMT2_OP2_SHIFT)#define FBT_FMT2_COND_SHIFT 25#define FBT_FMT2_COND_BA (0x8 << FBT_FMT2_COND_SHIFT)#define FBT_FMT2_COND_BL (0x3 << FBT_FMT2_COND_SHIFT)#define FBT_FMT2_COND_BGE (0xb << FBT_FMT2_COND_SHIFT)#define FBT_OP_RESTORE (FBT_OP2 | (0x3d << FBT_FMT3_OP3_SHIFT))#define FBT_OP_SAVE (FBT_OP2 | (0x3c << FBT_FMT3_OP3_SHIFT))#define FBT_OP_JMPL (FBT_OP2 | (0x38 << FBT_FMT3_OP3_SHIFT))#define FBT_OP_CALL FBT_OP1#define FBT_OP_SETHI (FBT_OP0 | FBT_FMT2_OP2_SETHI)#define FBT_OP_ADD (FBT_OP2 | (0x00 << FBT_FMT3_OP3_SHIFT))#define FBT_OP_OR (FBT_OP2 | (0x02 << FBT_FMT3_OP3_SHIFT))#define FBT_OP_SUB (FBT_OP2 | (0x04 << FBT_FMT3_OP3_SHIFT))#define FBT_OP_CC (FBT_OP2 | (0x10 << FBT_FMT3_OP3_SHIFT))#define FBT_OP_BA (FBT_OP0 | FBT_FMT2_OP2_BCC | FBT_FMT2_COND_BA)#define FBT_OP_BL (FBT_OP0 | FBT_FMT2_OP2_BCC | FBT_FMT2_COND_BL)#define FBT_OP_BGE (FBT_OP0 | FBT_FMT2_OP2_BCC | FBT_FMT2_COND_BGE)#define FBT_ORLO(rs, val, rd) \ (FBT_OP_OR | ((rs) << FBT_FMT3_RS1_SHIFT) | \ ((rd) << FBT_FMT3_RD_SHIFT) | FBT_FMT3_IMM | ((val) & FBT_IMM10_MASK))#define FBT_ORSIMM13(rs, val, rd) \ (FBT_OP_OR | ((rs) << FBT_FMT3_RS1_SHIFT) | \ ((rd) << FBT_FMT3_RD_SHIFT) | FBT_FMT3_IMM | ((val) & FBT_SIMM13_MASK))#define FBT_ADDSIMM13(rs, val, rd) \ (FBT_OP_ADD | ((rs) << FBT_FMT3_RS1_SHIFT) | \ ((rd) << FBT_FMT3_RD_SHIFT) | FBT_FMT3_IMM | ((val) & FBT_SIMM13_MASK))#define FBT_ADD(rs1, rs2, rd) \ (FBT_OP_ADD | ((rs1) << FBT_FMT3_RS1_SHIFT) | \ ((rs2) << FBT_FMT3_RS2_SHIFT) | ((rd) << FBT_FMT3_RD_SHIFT))#define FBT_CMP(rs1, rs2) \ (FBT_OP_SUB | FBT_OP_CC | ((rs1) << FBT_FMT3_RS1_SHIFT) | \ ((rs2) << FBT_FMT3_RS2_SHIFT) | (FBT_REG_G0 << FBT_FMT3_RD_SHIFT))#define FBT_MOV(rs, rd) \ (FBT_OP_OR | (FBT_REG_G0 << FBT_FMT3_RS1_SHIFT) | \ ((rs) << FBT_FMT3_RS2_SHIFT) | ((rd) << FBT_FMT3_RD_SHIFT))#define FBT_SETHI(val, reg) \ (FBT_OP_SETHI | (reg << FBT_FMT2_RD_SHIFT) | \ ((val >> FBT_IMM22_SHIFT) & FBT_IMM22_MASK))#define FBT_CALL(orig, dest) (FBT_OP_CALL | FBT_DISP30(orig, dest))#define FBT_RET \ (FBT_OP_JMPL | (FBT_REG_I7 << FBT_FMT3_RS1_SHIFT) | \ (FBT_REG_G0 << FBT_FMT3_RD_SHIFT) | FBT_FMT3_IMM | (sizeof (pc_t) << 1))#define FBT_SAVEIMM(rd, val, rs1) \ (FBT_OP_SAVE | ((rs1) << FBT_FMT3_RS1_SHIFT) | \ ((rd) << FBT_FMT3_RD_SHIFT) | FBT_FMT3_IMM | ((val) & FBT_SIMM13_MASK))#define FBT_RESTORE(rd, rs1, rs2) \ (FBT_OP_RESTORE | ((rs1) << FBT_FMT3_RS1_SHIFT) | \ ((rd) << FBT_FMT3_RD_SHIFT) | ((rs2) << FBT_FMT3_RS2_SHIFT))#define FBT_BA(orig, dest) (FBT_OP_BA | FBT_DISP22(orig, dest))#define FBT_BAA(orig, dest) (FBT_BA(orig, dest) | FBT_ANNUL)#define FBT_BL(orig, dest) (FBT_OP_BL | FBT_DISP22(orig, dest))#define FBT_BGE(orig, dest) (FBT_OP_BGE | FBT_DISP22(orig, dest))#define FBT_BDEST(va, instr) ((uintptr_t)(va) + \ (((int32_t)(((instr) & FBT_DISP22_MASK) << 10)) >> 8))/* * We're only going to treat a save as safe if (a) both rs1 and rd are * %sp and (b) if the instruction has a simm, the value isn't 0. */#define FBT_IS_SAVE(instr) \ (FBT_FMT3_OP(instr) == FBT_OP_SAVE && \ FBT_FMT3_RD(instr) == FBT_REG_O6 && \ FBT_FMT3_RS1(instr) == FBT_REG_O6 && \ !(FBT_FMT3_ISIMM(instr) && FBT_FMT3_SIMM13(instr) == 0))#define FBT_IS_BA(instr) (((instr) & ~FBT_DISP22_MASK) == FBT_OP_BA)#define FBT_ENTRY "entry"#define FBT_RETURN "return"#define FBT_ESTIMATE_ID (UINT32_MAX)#define FBT_COUNTER(id, count) if ((id) != FBT_ESTIMATE_ID) (count)++#define FBT_ENTENT_MAXSIZE (12 * sizeof (uint32_t))#define FBT_RETENT_MAXSIZE (11 * sizeof (uint32_t))#define FBT_RETLENT_MAXSIZE (23 * sizeof (uint32_t))#define FBT_ENT_MAXSIZE \ MAX(MAX(FBT_ENTENT_MAXSIZE, FBT_RETENT_MAXSIZE), FBT_RETLENT_MAXSIZE)typedef struct fbt_probe { char *fbtp_name; dtrace_id_t fbtp_id; uintptr_t fbtp_addr; struct modctl *fbtp_ctl; int fbtp_loadcnt; int fbtp_symndx; int fbtp_primary; int fbtp_return; uint32_t *fbtp_patchpoint; uint32_t fbtp_patchval; uint32_t fbtp_savedval; struct fbt_probe *fbtp_next;} fbt_probe_t;typedef struct fbt_trampoline { uintptr_t fbtt_va; uintptr_t fbtt_limit; uintptr_t fbtt_next;} fbt_trampoline_t;static caddr_tfbt_trampoline_map(uintptr_t tramp, size_t size){ uintptr_t offs; ASSERT(fbt_trampoline_window == NULL); ASSERT(fbt_trampoline_size == 0); ASSERT(fbt_trampoline == NULL); size += tramp & PAGEOFFSET; fbt_trampoline = tramp & PAGEMASK; fbt_trampoline_size = (size + PAGESIZE - 1) & PAGEMASK; fbt_trampoline_window = vmem_alloc(heap_arena, fbt_trampoline_size, VM_SLEEP); for (offs = 0; offs < fbt_trampoline_size; offs += PAGESIZE) { hat_devload(kas.a_hat, fbt_trampoline_window + offs, PAGESIZE, hat_getpfnum(kas.a_hat, (caddr_t)fbt_trampoline + offs), PROT_READ | PROT_WRITE, HAT_LOAD_LOCK | HAT_LOAD_NOCONSIST); } return (fbt_trampoline_window + (tramp & PAGEOFFSET));}static voidfbt_trampoline_unmap(){ ASSERT(fbt_trampoline_window != NULL); ASSERT(fbt_trampoline_size != 0); ASSERT(fbt_trampoline != NULL); membar_enter(); sync_icache((caddr_t)fbt_trampoline, fbt_trampoline_size); sync_icache(fbt_trampoline_window, fbt_trampoline_size); hat_unload(kas.a_hat, fbt_trampoline_window, fbt_trampoline_size, HAT_UNLOAD_UNLOCK); vmem_free(heap_arena, fbt_trampoline_window, fbt_trampoline_size); fbt_trampoline_window = NULL; fbt_trampoline = NULL; fbt_trampoline_size = 0;}static uintptr_tfbt_patch_entry(uint32_t *instr, uint32_t id, fbt_trampoline_t *tramp, int nargs){ uint32_t *tinstr = (uint32_t *)tramp->fbtt_next; uint32_t save = *instr; uintptr_t va = tramp->fbtt_va; uintptr_t base = tramp->fbtt_next; if (tramp->fbtt_next + FBT_ENTENT_MAXSIZE > tramp->fbtt_limit) { /* * There isn't sufficient room for this entry; return failure. */ return (0); } FBT_COUNTER(id, fbt_entry); if (FBT_IS_SAVE(save)) { *tinstr++ = save; } else { *tinstr++ = FBT_SAVEIMM(FBT_REG_O6, -SA(MINFRAME), FBT_REG_O6); } 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 (nargs >= 1) *tinstr++ = FBT_MOV(FBT_REG_I0, FBT_REG_O1); if (nargs >= 2) *tinstr++ = FBT_MOV(FBT_REG_I1, FBT_REG_O2); if (nargs >= 3) *tinstr++ = FBT_MOV(FBT_REG_I2, FBT_REG_O3); if (nargs >= 4) *tinstr++ = FBT_MOV(FBT_REG_I3, FBT_REG_O4); if (nargs >= 5) *tinstr++ = FBT_MOV(FBT_REG_I4, FBT_REG_O5); if (FBT_IS_SAVE(save)) { uintptr_t ret = (uintptr_t)instr - 4; *tinstr++ = FBT_SETHI(ret, FBT_REG_G1); *tinstr = FBT_CALL((uintptr_t)tinstr - base + va, dtrace_probe); tinstr++; *tinstr++ = FBT_ORLO(FBT_REG_G1, ret, FBT_REG_O7); } else { uintptr_t slot = *--tinstr; uintptr_t ret = (uintptr_t)instr + 4; uint32_t delay = *instr; *tinstr = FBT_CALL((uintptr_t)tinstr - base + va, dtrace_probe); tinstr++; *tinstr++ = slot; *tinstr++ = FBT_RESTORE(FBT_REG_G0, FBT_REG_G0, FBT_REG_G0); if (FBT_IS_BA(save)) { /* * This is a special case: we'll return directly * to the destination of the branch, putting the * save in the delay slot. */ ret = FBT_BDEST(instr, *instr); delay = *(instr + 1); } *tinstr = FBT_BA((uintptr_t)tinstr - base + va, ret); tinstr++; *tinstr++ = delay; } tramp->fbtt_va += (uintptr_t)tinstr - tramp->fbtt_next; tramp->fbtt_next = (uintptr_t)tinstr; return (1);}/* * We are patching control-transfer/restore couplets. There are two variants * of couplet: * * (a) jmpl rs1 + (rs2 | offset), rd * restore rs1, rs2 | imm, rd * * (b) call displacement * restore rs1, rs2 | imm, rd * * If rd from the jmpl in (a) is something other than %g0 (a ret) or %o7 (a * call through a register), we fail. * * Note that rs1 and rs2 in the restore are potentially outputs and/or globals. * Because these registers cannot be relied upon across the call to * dtrace_probe(), we move rs1 into an unused local, ls0, and rs2 into an * unused local, ls1, and restructure the restore to be: * * restore ls0, ls1, rd * * Likewise, rs1 and rs2 in the jmpl of case (a) may be outputs and/or globals. * If the jmpl uses outputs or globals, we restructure it to be: * * jmpl ls2 + (ls3 | offset), (%g0 | %o7) * *//*ARGSUSED*/static intfbt_canpatch_return(uint32_t *instr, int offset){ int rd; if (FBT_FMT3_OP(*(instr + 1)) != FBT_OP_RESTORE) return (0); if (FBT_FMT1_OP(*instr) == FBT_OP_CALL) return (1); if (FBT_FMT3_OP(*instr) != FBT_OP_JMPL) return (0); rd = FBT_FMT3_RD(*instr); if (rd == FBT_REG_I7 || rd == FBT_REG_O7 || rd == FBT_REG_G0) return (1); /* * We have encountered a jmpl that is storing the calling %pc in * some register besides %i7, %o7 or %g0. This is strange; emit * a warning and fail. */ cmn_err(CE_NOTE, "strange jmpl at %p", (void *)instr); return (0);}static intfbt_canpatch_retl(uint32_t *instr, int offset){ if (FBT_FMT1_OP(*instr) == FBT_OP_CALL || (FBT_FMT3_OP(*instr) == FBT_OP_JMPL && FBT_FMT3_RD(*instr) == FBT_REG_O7)) { /* * If this is a call (or a jmpl that links into %o7), we can * patch it iff the next instruction uses %o7 as a destination * register. Because there is an ABI responsibility to * restore %o7 to the value before the call/jmpl, we don't * particularly care how this routine is managing to restore * it (mov, add, ld or divx for all we care). If it doesn't * seem to be restoring it at all, however, we'll refuse * to patch it. */ uint32_t delay = *(instr + 1); uint32_t op, rd; op = FBT_FMT1_OP(delay); rd = FBT_FMT3_RD(delay); if (op != FBT_OP2 || rd != FBT_REG_O7) { cmn_err(CE_NOTE, "strange leaf jmpl/call " "delay at %p", (void *)(instr + 1)); return (0); } return (1); } if (offset == sizeof (uint32_t)) { /* * If this is the second instruction in the function, we're * going to allow it to be patched if the first instruction * is a patchable return-from-leaf instruction. */ if (fbt_canpatch_retl(instr - 1, 0)) return (1); } if (FBT_FMT3_OP(*instr) != FBT_OP_JMPL) return (0); if (FBT_FMT3_RD(*instr) != FBT_REG_G0) return (0); return (1);}/*ARGSUSED*/static uint32_tfbt_patch_return(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; uint32_t cti = *instr, restore = *(instr + 1), rs1, dest; uintptr_t va = tramp->fbtt_va; uintptr_t base = tramp->fbtt_next; uint32_t locals[FBT_REG_NLOCALS], local; if (tramp->fbtt_next + FBT_RETENT_MAXSIZE > tramp->fbtt_limit) { /* * There isn't sufficient room for this entry; return failure. */ return (FBT_ILLTRAP); } FBT_COUNTER(id, fbt_ret); FBT_REG_INITLOCALS(local, locals); /* * Mark the locals used in the jmpl. */ if (FBT_FMT3_OP(cti) == FBT_OP_JMPL) { uint32_t rs1 = FBT_FMT3_RS1(cti); FBT_REG_MARKLOCAL(locals, rs1); if (!FBT_FMT3_ISIMM(cti)) { uint32_t rs2 = FBT_FMT3_RS2(cti); FBT_REG_MARKLOCAL(locals, rs2);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -