Path: blob/main/cddl/contrib/opensolaris/lib/libdtrace/i386/dt_isadep.c
39563 views
/*1* CDDL HEADER START2*3* The contents of this file are subject to the terms of the4* Common Development and Distribution License (the "License").5* You may not use this file except in compliance with the License.6*7* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE8* or http://www.opensolaris.org/os/licensing.9* See the License for the specific language governing permissions10* and limitations under the License.11*12* When distributing Covered Code, include this CDDL HEADER in each13* file and include the License file at usr/src/OPENSOLARIS.LICENSE.14* If applicable, add the following below this CDDL HEADER, with the15* fields enclosed by brackets "[]" replaced with your own identifying16* information: Portions Copyright [yyyy] [name of copyright owner]17*18* CDDL HEADER END19*/2021/*22* Copyright 2009 Sun Microsystems, Inc. All rights reserved.23* Use is subject to license terms.24*/2526/*27* Copyright (c) 2012 by Delphix. All rights reserved.28*/2930#include <stdlib.h>31#include <assert.h>32#include <errno.h>33#include <string.h>34#include <libgen.h>3536#include <dt_impl.h>37#include <dt_pid.h>3839#include <dis_tables.h>4041#ifdef __FreeBSD__42#include <libproc.h>43#include <libproc_compat.h>44#endif4546#define DT_POPL_EBP 0x5d47#define DT_RET 0xc348#define DT_RET16 0xc249#define DT_LEAVE 0xc950#define DT_JMP32 0xe951#define DT_JMP8 0xeb52#define DT_REP 0xf35354#define DT_MOVL_EBP_ESP 0xe58b5556#define DT_ISJ32(op16) (((op16) & 0xfff0) == 0x0f80)57#define DT_ISJ8(op8) (((op8) & 0xf0) == 0x70)5859#define DT_MODRM_REG(modrm) (((modrm) >> 3) & 0x7)6061static int dt_instr_size(uchar_t *, dtrace_hdl_t *, pid_t, uintptr_t, char);6263/*ARGSUSED*/64int65dt_pid_create_entry_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp,66fasttrap_probe_spec_t *ftp, const GElf_Sym *symp)67{68ftp->ftps_type = DTFTP_ENTRY;69ftp->ftps_pc = (uintptr_t)symp->st_value;70ftp->ftps_size = (size_t)symp->st_size;71ftp->ftps_noffs = 1;72ftp->ftps_offs[0] = 0;7374if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) {75dt_dprintf("fasttrap probe creation ioctl failed: %s\n",76strerror(errno));77return (dt_set_errno(dtp, errno));78}7980return (1);81}8283static int84dt_pid_has_jump_table(struct ps_prochandle *P, dtrace_hdl_t *dtp,85uint8_t *text, fasttrap_probe_spec_t *ftp, const GElf_Sym *symp)86{87ulong_t i;88int size;89#ifdef illumos90pid_t pid = Pstatus(P)->pr_pid;91char dmodel = Pstatus(P)->pr_dmodel;92#else93pid_t pid = proc_getpid(P);94char dmodel = proc_getmodel(P);95#endif9697/*98* Take a pass through the function looking for a register-dependant99* jmp instruction. This could be a jump table so we have to be100* ultra conservative.101*/102for (i = 0; i < ftp->ftps_size; i += size) {103size = dt_instr_size(&text[i], dtp, pid, symp->st_value + i,104dmodel);105106/*107* Assume the worst if we hit an illegal instruction.108*/109if (size <= 0) {110dt_dprintf("error at %#lx (assuming jump table)\n", i);111return (1);112}113114#ifdef notyet115/*116* Register-dependant jmp instructions start with a 0xff byte117* and have the modrm.reg field set to 4. They can have an118* optional REX prefix on the 64-bit ISA.119*/120if ((text[i] == 0xff && DT_MODRM_REG(text[i + 1]) == 4) ||121(dmodel == PR_MODEL_LP64 && (text[i] & 0xf0) == 0x40 &&122text[i + 1] == 0xff && DT_MODRM_REG(text[i + 2]) == 4)) {123dt_dprintf("found a suspected jump table at %s:%lx\n",124ftp->ftps_func, i);125return (1);126}127#endif128}129130return (0);131}132133/*ARGSUSED*/134int135dt_pid_create_return_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp,136fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, uint64_t *stret)137{138uint8_t *text;139ulong_t i, end;140int size;141#ifdef illumos142pid_t pid = Pstatus(P)->pr_pid;143char dmodel = Pstatus(P)->pr_dmodel;144#else145pid_t pid = proc_getpid(P);146char dmodel = proc_getmodel(P);147#endif148149/*150* We allocate a few extra bytes at the end so we don't have to check151* for overrunning the buffer.152*/153if ((text = calloc(1, symp->st_size + 4)) == NULL) {154dt_dprintf("mr sparkle: malloc() failed\n");155return (DT_PROC_ERR);156}157158if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) {159dt_dprintf("mr sparkle: Pread() failed\n");160free(text);161return (DT_PROC_ERR);162}163164ftp->ftps_type = DTFTP_RETURN;165ftp->ftps_pc = (uintptr_t)symp->st_value;166ftp->ftps_size = (size_t)symp->st_size;167ftp->ftps_noffs = 0;168169/*170* If there's a jump table in the function we're only willing to171* instrument these specific (and equivalent) instruction sequences:172* leave173* [rep] ret174* and175* movl %ebp,%esp176* popl %ebp177* [rep] ret178*179* We do this to avoid accidentally interpreting jump table180* offsets as actual instructions.181*/182if (dt_pid_has_jump_table(P, dtp, text, ftp, symp)) {183for (i = 0, end = ftp->ftps_size; i < end; i += size) {184size = dt_instr_size(&text[i], dtp, pid,185symp->st_value + i, dmodel);186187/* bail if we hit an invalid opcode */188if (size <= 0)189break;190191if (text[i] == DT_LEAVE && text[i + 1] == DT_RET) {192dt_dprintf("leave/ret at %lx\n", i + 1);193ftp->ftps_offs[ftp->ftps_noffs++] = i + 1;194size = 2;195} else if (text[i] == DT_LEAVE &&196text[i + 1] == DT_REP && text[i + 2] == DT_RET) {197dt_dprintf("leave/rep ret at %lx\n", i + 1);198ftp->ftps_offs[ftp->ftps_noffs++] = i + 1;199size = 3;200} else if (*(uint16_t *)&text[i] == DT_MOVL_EBP_ESP &&201text[i + 2] == DT_POPL_EBP &&202text[i + 3] == DT_RET) {203dt_dprintf("movl/popl/ret at %lx\n", i + 3);204ftp->ftps_offs[ftp->ftps_noffs++] = i + 3;205size = 4;206} else if (*(uint16_t *)&text[i] == DT_MOVL_EBP_ESP &&207text[i + 2] == DT_POPL_EBP &&208text[i + 3] == DT_REP &&209text[i + 4] == DT_RET) {210dt_dprintf("movl/popl/rep ret at %lx\n", i + 3);211ftp->ftps_offs[ftp->ftps_noffs++] = i + 3;212size = 5;213}214}215} else {216for (i = 0, end = ftp->ftps_size; i < end; i += size) {217size = dt_instr_size(&text[i], dtp, pid,218symp->st_value + i, dmodel);219220/* bail if we hit an invalid opcode */221if (size <= 0)222break;223224/* ordinary ret */225if (size == 1 && text[i] == DT_RET)226goto is_ret;227228/* two-byte ret */229if (size == 2 && text[i] == DT_REP &&230text[i + 1] == DT_RET)231goto is_ret;232233/* ret <imm16> */234if (size == 3 && text[i] == DT_RET16)235goto is_ret;236237/* two-byte ret <imm16> */238if (size == 4 && text[i] == DT_REP &&239text[i + 1] == DT_RET16)240goto is_ret;241242/* 32-bit displacement jmp outside of the function */243if (size == 5 && text[i] == DT_JMP32 && symp->st_size <=244(uintptr_t)(i + size + *(int32_t *)&text[i + 1]))245goto is_ret;246247/* 8-bit displacement jmp outside of the function */248if (size == 2 && text[i] == DT_JMP8 && symp->st_size <=249(uintptr_t)(i + size + *(int8_t *)&text[i + 1]))250goto is_ret;251252/* 32-bit disp. conditional jmp outside of the func. */253if (size == 6 && DT_ISJ32(*(uint16_t *)&text[i]) &&254symp->st_size <=255(uintptr_t)(i + size + *(int32_t *)&text[i + 2]))256goto is_ret;257258/* 8-bit disp. conditional jmp outside of the func. */259if (size == 2 && DT_ISJ8(text[i]) && symp->st_size <=260(uintptr_t)(i + size + *(int8_t *)&text[i + 1]))261goto is_ret;262263continue;264is_ret:265dt_dprintf("return at offset %lx\n", i);266ftp->ftps_offs[ftp->ftps_noffs++] = i;267}268}269270free(text);271if (ftp->ftps_noffs > 0) {272if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) {273dt_dprintf("fasttrap probe creation ioctl failed: %s\n",274strerror(errno));275return (dt_set_errno(dtp, errno));276}277}278279return (ftp->ftps_noffs);280}281282/*ARGSUSED*/283int284dt_pid_create_offset_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp,285fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, ulong_t off)286{287ftp->ftps_type = DTFTP_OFFSETS;288ftp->ftps_pc = (uintptr_t)symp->st_value;289ftp->ftps_size = (size_t)symp->st_size;290ftp->ftps_noffs = 1;291292if (strcmp("-", ftp->ftps_func) == 0) {293ftp->ftps_offs[0] = off;294} else {295uint8_t *text;296ulong_t i;297int size;298#ifdef illumos299pid_t pid = Pstatus(P)->pr_pid;300char dmodel = Pstatus(P)->pr_dmodel;301#else302pid_t pid = proc_getpid(P);303char dmodel = proc_getmodel(P);304#endif305306if ((text = malloc(symp->st_size)) == NULL) {307dt_dprintf("mr sparkle: malloc() failed\n");308return (DT_PROC_ERR);309}310311if (Pread(P, text, symp->st_size, symp->st_value) !=312symp->st_size) {313dt_dprintf("mr sparkle: Pread() failed\n");314free(text);315return (DT_PROC_ERR);316}317318/*319* We can't instrument offsets in functions with jump tables320* as we might interpret a jump table offset as an321* instruction.322*/323if (dt_pid_has_jump_table(P, dtp, text, ftp, symp)) {324free(text);325return (0);326}327328for (i = 0; i < symp->st_size; i += size) {329if (i == off) {330ftp->ftps_offs[0] = i;331break;332}333334/*335* If we've passed the desired offset without a336* match, then the given offset must not lie on a337* instruction boundary.338*/339if (i > off) {340free(text);341return (DT_PROC_ALIGN);342}343344size = dt_instr_size(&text[i], dtp, pid,345symp->st_value + i, dmodel);346347/*348* If we hit an invalid instruction, bail as if we349* couldn't find the offset.350*/351if (size <= 0) {352free(text);353return (DT_PROC_ALIGN);354}355}356357free(text);358}359360if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) {361dt_dprintf("fasttrap probe creation ioctl failed: %s\n",362strerror(errno));363return (dt_set_errno(dtp, errno));364}365366return (ftp->ftps_noffs);367}368369/*ARGSUSED*/370int371dt_pid_create_glob_offset_probes(struct ps_prochandle *P, dtrace_hdl_t *dtp,372fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, const char *pattern)373{374uint8_t *text;375int size;376ulong_t i, end = symp->st_size;377#ifdef illumos378pid_t pid = Pstatus(P)->pr_pid;379char dmodel = Pstatus(P)->pr_dmodel;380#else381pid_t pid = proc_getpid(P);382char dmodel = proc_getmodel(P);383#endif384385ftp->ftps_type = DTFTP_OFFSETS;386ftp->ftps_pc = (uintptr_t)symp->st_value;387ftp->ftps_size = (size_t)symp->st_size;388ftp->ftps_noffs = 0;389390if ((text = malloc(symp->st_size)) == NULL) {391dt_dprintf("mr sparkle: malloc() failed\n");392return (DT_PROC_ERR);393}394395if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) {396dt_dprintf("mr sparkle: Pread() failed\n");397free(text);398return (DT_PROC_ERR);399}400401/*402* We can't instrument offsets in functions with jump tables as403* we might interpret a jump table offset as an instruction.404*/405if (dt_pid_has_jump_table(P, dtp, text, ftp, symp)) {406free(text);407return (0);408}409410if (strcmp("*", pattern) == 0) {411for (i = 0; i < end; i += size) {412ftp->ftps_offs[ftp->ftps_noffs++] = i;413414size = dt_instr_size(&text[i], dtp, pid,415symp->st_value + i, dmodel);416417/* bail if we hit an invalid opcode */418if (size <= 0)419break;420}421} else {422char name[sizeof (i) * 2 + 1];423424for (i = 0; i < end; i += size) {425(void) snprintf(name, sizeof (name), "%lx", i);426if (gmatch(name, pattern))427ftp->ftps_offs[ftp->ftps_noffs++] = i;428429size = dt_instr_size(&text[i], dtp, pid,430symp->st_value + i, dmodel);431432/* bail if we hit an invalid opcode */433if (size <= 0)434break;435}436}437438free(text);439if (ftp->ftps_noffs > 0) {440if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) {441dt_dprintf("fasttrap probe creation ioctl failed: %s\n",442strerror(errno));443return (dt_set_errno(dtp, errno));444}445}446447return (ftp->ftps_noffs);448}449450typedef struct dtrace_dis {451uchar_t *instr;452dtrace_hdl_t *dtp;453pid_t pid;454uintptr_t addr;455} dtrace_dis_t;456457static int458dt_getbyte(void *data)459{460dtrace_dis_t *dis = data;461int ret = *dis->instr;462463if (ret == FASTTRAP_INSTR) {464fasttrap_instr_query_t instr;465466instr.ftiq_pid = dis->pid;467instr.ftiq_pc = dis->addr;468469/*470* If we hit a byte that looks like the fasttrap provider's471* trap instruction (which doubles as the breakpoint472* instruction for debuggers) we need to query the kernel473* for the real value. This may just be part of an immediate474* value so there's no need to return an error if the475* kernel doesn't know about this address.476*/477if (ioctl(dis->dtp->dt_ftfd, FASTTRAPIOC_GETINSTR, &instr) == 0)478ret = instr.ftiq_instr;479}480481dis->addr++;482dis->instr++;483484return (ret);485}486487static int488dt_instr_size(uchar_t *instr, dtrace_hdl_t *dtp, pid_t pid, uintptr_t addr,489char dmodel)490{491dtrace_dis_t data;492dis86_t x86dis;493uint_t cpu_mode;494495data.instr = instr;496data.dtp = dtp;497data.pid = pid;498data.addr = addr;499500x86dis.d86_data = &data;501x86dis.d86_get_byte = dt_getbyte;502x86dis.d86_check_func = NULL;503504cpu_mode = (dmodel == PR_MODEL_ILP32) ? SIZE32 : SIZE64;505506if (dtrace_disx86(&x86dis, cpu_mode) != 0)507return (-1);508509/*510* If the instruction was a single-byte breakpoint, there may be511* another debugger attached to this process. The original instruction512* can't be recovered so this must fail.513*/514if (x86dis.d86_len == 1 &&515(uchar_t)x86dis.d86_bytes[0] == FASTTRAP_INSTR)516return (-1);517518return (x86dis.d86_len);519}520521522