1210198Srpaulo/* 2210198Srpaulo * CDDL HEADER START 3210198Srpaulo * 4210198Srpaulo * The contents of this file are subject to the terms of the 5210198Srpaulo * Common Development and Distribution License, Version 1.0 only 6210198Srpaulo * (the "License"). You may not use this file except in compliance 7210198Srpaulo * with the License. 8210198Srpaulo * 9210198Srpaulo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10210198Srpaulo * or http://www.opensolaris.org/os/licensing. 11210198Srpaulo * See the License for the specific language governing permissions 12210198Srpaulo * and limitations under the License. 13210198Srpaulo * 14210198Srpaulo * When distributing Covered Code, include this CDDL HEADER in each 15210198Srpaulo * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16210198Srpaulo * If applicable, add the following below this CDDL HEADER, with the 17210198Srpaulo * fields enclosed by brackets "[]" replaced with your own identifying 18210198Srpaulo * information: Portions Copyright [yyyy] [name of copyright owner] 19210198Srpaulo * 20210198Srpaulo * CDDL HEADER END 21210198Srpaulo */ 22210198Srpaulo/* 23210198Srpaulo * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24210198Srpaulo * Use is subject to license terms. 25210198Srpaulo */ 26210198Srpaulo 27210198Srpaulo#pragma ident "%Z%%M% %I% %E% SMI" 28210198Srpaulo 29210198Srpaulo#include <stdlib.h> 30210198Srpaulo#include <assert.h> 31210198Srpaulo#include <errno.h> 32210198Srpaulo#include <string.h> 33210198Srpaulo#include <libgen.h> 34210198Srpaulo 35210198Srpaulo#include <dt_impl.h> 36210198Srpaulo#include <dt_pid.h> 37210198Srpaulo 38210198Srpaulo#define OP(x) ((x) >> 30) 39210198Srpaulo#define OP2(x) (((x) >> 22) & 0x07) 40210198Srpaulo#define COND(x) (((x) >> 25) & 0x0f) 41210198Srpaulo#define A(x) (((x) >> 29) & 0x01) 42210198Srpaulo 43210198Srpaulo#define OP_BRANCH 0 44210198Srpaulo 45210198Srpaulo#define OP2_BPcc 0x1 46210198Srpaulo#define OP2_Bicc 0x2 47210198Srpaulo#define OP2_BPr 0x3 48210198Srpaulo#define OP2_FBPfcc 0x5 49210198Srpaulo#define OP2_FBfcc 0x6 50210198Srpaulo 51210198Srpaulo/*ARGSUSED*/ 52210198Srpauloint 53210198Srpaulodt_pid_create_entry_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, 54210198Srpaulo fasttrap_probe_spec_t *ftp, const GElf_Sym *symp) 55210198Srpaulo{ 56210198Srpaulo ftp->ftps_type = DTFTP_ENTRY; 57210198Srpaulo ftp->ftps_pc = (uintptr_t)symp->st_value; 58210198Srpaulo ftp->ftps_size = (size_t)symp->st_size; 59210198Srpaulo ftp->ftps_noffs = 1; 60210198Srpaulo ftp->ftps_offs[0] = 0; 61210198Srpaulo 62210198Srpaulo if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { 63210198Srpaulo dt_dprintf("fasttrap probe creation ioctl failed: %s\n", 64210198Srpaulo strerror(errno)); 65210198Srpaulo return (dt_set_errno(dtp, errno)); 66210198Srpaulo } 67210198Srpaulo 68210198Srpaulo return (1); 69210198Srpaulo} 70210198Srpaulo 71210198Srpauloint 72210198Srpaulodt_pid_create_return_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, 73210198Srpaulo fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, uint64_t *stret) 74210198Srpaulo{ 75210198Srpaulo 76210198Srpaulo uint32_t *text; 77210198Srpaulo int i; 78210198Srpaulo int srdepth = 0; 79210198Srpaulo 80210198Srpaulo if ((text = malloc(symp->st_size + 4)) == NULL) { 81210198Srpaulo dt_dprintf("mr sparkle: malloc() failed\n"); 82210198Srpaulo return (DT_PROC_ERR); 83210198Srpaulo } 84210198Srpaulo 85210198Srpaulo if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) { 86210198Srpaulo dt_dprintf("mr sparkle: Pread() failed\n"); 87210198Srpaulo free(text); 88210198Srpaulo return (DT_PROC_ERR); 89210198Srpaulo } 90210198Srpaulo 91210198Srpaulo /* 92210198Srpaulo * Leave a dummy instruction in the last slot to simplify edge 93210198Srpaulo * conditions. 94210198Srpaulo */ 95210198Srpaulo text[symp->st_size / 4] = 0; 96210198Srpaulo 97210198Srpaulo ftp->ftps_type = DTFTP_RETURN; 98210198Srpaulo ftp->ftps_pc = symp->st_value; 99210198Srpaulo ftp->ftps_size = symp->st_size; 100210198Srpaulo ftp->ftps_noffs = 0; 101210198Srpaulo 102210198Srpaulo for (i = 0; i < symp->st_size / 4; i++) { 103210198Srpaulo /* 104210198Srpaulo * If we encounter an existing tracepoint, query the 105210198Srpaulo * kernel to find out the instruction that was 106210198Srpaulo * replaced at this spot. 107210198Srpaulo */ 108210198Srpaulo while (text[i] == FASTTRAP_INSTR) { 109210198Srpaulo fasttrap_instr_query_t instr; 110210198Srpaulo 111210198Srpaulo instr.ftiq_pid = Pstatus(P)->pr_pid; 112210198Srpaulo instr.ftiq_pc = symp->st_value + i * 4; 113210198Srpaulo 114210198Srpaulo if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_GETINSTR, 115210198Srpaulo &instr) != 0) { 116210198Srpaulo 117210198Srpaulo if (errno == ESRCH || errno == ENOENT) { 118210198Srpaulo if (Pread(P, &text[i], 4, 119210198Srpaulo instr.ftiq_pc) != 4) { 120210198Srpaulo dt_dprintf("mr sparkle: " 121210198Srpaulo "Pread() failed\n"); 122210198Srpaulo free(text); 123210198Srpaulo return (DT_PROC_ERR); 124210198Srpaulo } 125210198Srpaulo continue; 126210198Srpaulo } 127210198Srpaulo 128210198Srpaulo free(text); 129210198Srpaulo dt_dprintf("mr sparkle: getinstr query " 130210198Srpaulo "failed: %s\n", strerror(errno)); 131210198Srpaulo return (DT_PROC_ERR); 132210198Srpaulo } 133210198Srpaulo 134210198Srpaulo text[i] = instr.ftiq_instr; 135210198Srpaulo break; 136210198Srpaulo } 137210198Srpaulo 138210198Srpaulo /* save */ 139210198Srpaulo if ((text[i] & 0xc1f80000) == 0x81e00000) { 140210198Srpaulo srdepth++; 141210198Srpaulo continue; 142210198Srpaulo } 143210198Srpaulo 144210198Srpaulo /* restore */ 145210198Srpaulo if ((text[i] & 0xc1f80000) == 0x81e80000) { 146210198Srpaulo srdepth--; 147210198Srpaulo continue; 148210198Srpaulo } 149210198Srpaulo 150210198Srpaulo if (srdepth > 0) { 151210198Srpaulo /* ret */ 152210198Srpaulo if (text[i] == 0x81c7e008) 153210198Srpaulo goto is_ret; 154210198Srpaulo 155210198Srpaulo /* return */ 156210198Srpaulo if (text[i] == 0x81cfe008) 157210198Srpaulo goto is_ret; 158210198Srpaulo 159210198Srpaulo /* call or jmpl w/ restore in the slot */ 160210198Srpaulo if (((text[i] & 0xc0000000) == 0x40000000 || 161210198Srpaulo (text[i] & 0xc1f80000) == 0x81c00000) && 162210198Srpaulo (text[i + 1] & 0xc1f80000) == 0x81e80000) 163210198Srpaulo goto is_ret; 164210198Srpaulo 165210198Srpaulo /* call to one of the stret routines */ 166210198Srpaulo if ((text[i] & 0xc0000000) == 0x40000000) { 167210198Srpaulo int32_t disp = text[i] << 2; 168210198Srpaulo uint64_t dest = ftp->ftps_pc + i * 4 + disp; 169210198Srpaulo 170210198Srpaulo dt_dprintf("dest = %llx\n", (u_longlong_t)dest); 171210198Srpaulo 172210198Srpaulo if (dest == stret[0] || dest == stret[1] || 173210198Srpaulo dest == stret[2] || dest == stret[3]) 174210198Srpaulo goto is_ret; 175210198Srpaulo } 176210198Srpaulo } else { 177210198Srpaulo /* external call */ 178210198Srpaulo if ((text[i] & 0xc0000000) == 0x40000000) { 179210198Srpaulo int32_t dst = text[i] << 2; 180210198Srpaulo 181210198Srpaulo dst += i * 4; 182210198Srpaulo 183210198Srpaulo if ((uintptr_t)dst >= (uintptr_t)symp->st_size) 184210198Srpaulo goto is_ret; 185210198Srpaulo } 186210198Srpaulo 187210198Srpaulo /* jmpl into %g0 -- this includes the retl pseudo op */ 188210198Srpaulo if ((text[i] & 0xfff80000) == 0x81c00000) 189210198Srpaulo goto is_ret; 190210198Srpaulo 191210198Srpaulo /* external branch -- possible return site */ 192210198Srpaulo if (OP(text[i]) == OP_BRANCH) { 193210198Srpaulo int32_t dst; 194210198Srpaulo int baa; 195210198Srpaulo 196210198Srpaulo switch (OP2(text[i])) { 197210198Srpaulo case OP2_BPcc: 198210198Srpaulo dst = text[i] & 0x7ffff; 199210198Srpaulo dst <<= 13; 200210198Srpaulo dst >>= 11; 201210198Srpaulo 202210198Srpaulo baa = COND(text[i]) == 8 && A(text[i]); 203210198Srpaulo break; 204210198Srpaulo case OP2_Bicc: 205210198Srpaulo dst = text[i] & 0x3fffff; 206210198Srpaulo dst <<= 10; 207210198Srpaulo dst >>= 8; 208210198Srpaulo 209210198Srpaulo baa = COND(text[i]) == 8 && A(text[i]); 210210198Srpaulo break; 211210198Srpaulo case OP2_BPr: 212210198Srpaulo dst = (((text[i]) >> 6) & 0xc000) | 213210198Srpaulo ((text[i]) & 0x3fff); 214210198Srpaulo dst <<= 16; 215210198Srpaulo dst >>= 14; 216210198Srpaulo 217210198Srpaulo baa = 0; 218210198Srpaulo break; 219210198Srpaulo case OP2_FBPfcc: 220210198Srpaulo dst = text[i] & 0x7ffff; 221210198Srpaulo dst <<= 13; 222210198Srpaulo dst >>= 11; 223210198Srpaulo 224210198Srpaulo baa = COND(text[i]) == 8 && A(text[i]); 225210198Srpaulo break; 226210198Srpaulo case OP2_FBfcc: 227210198Srpaulo dst = text[i] & 0x3fffff; 228210198Srpaulo dst <<= 10; 229210198Srpaulo dst >>= 8; 230210198Srpaulo 231210198Srpaulo baa = COND(text[i]) == 8 && A(text[i]); 232210198Srpaulo break; 233210198Srpaulo default: 234210198Srpaulo continue; 235210198Srpaulo } 236210198Srpaulo 237210198Srpaulo dst += i * 4; 238210198Srpaulo 239210198Srpaulo /* 240210198Srpaulo * Interpret branches outside of the function's 241210198Srpaulo * bounds as potential return sites. If the 242210198Srpaulo * branch is a ba,a don't skip the instruction 243210198Srpaulo * in the delay slot. 244210198Srpaulo */ 245210198Srpaulo if ((uintptr_t)dst >= 246210198Srpaulo (uintptr_t)symp->st_size) { 247210198Srpaulo if (baa) 248210198Srpaulo goto is_ret_baa; 249210198Srpaulo else 250210198Srpaulo goto is_ret; 251210198Srpaulo } 252210198Srpaulo } 253210198Srpaulo } 254210198Srpaulo 255210198Srpaulo continue; 256210198Srpaulois_ret: 257210198Srpaulo i++; 258210198Srpaulois_ret_baa: 259210198Srpaulo dt_dprintf("return at offset %x\n", i * 4); 260210198Srpaulo ftp->ftps_offs[ftp->ftps_noffs++] = i * 4; 261210198Srpaulo } 262210198Srpaulo 263210198Srpaulo free(text); 264210198Srpaulo if (ftp->ftps_noffs > 0) { 265210198Srpaulo if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { 266210198Srpaulo dt_dprintf("fasttrap probe creation ioctl failed: %s\n", 267210198Srpaulo strerror(errno)); 268210198Srpaulo return (dt_set_errno(dtp, errno)); 269210198Srpaulo } 270210198Srpaulo } 271210198Srpaulo 272210198Srpaulo 273210198Srpaulo return (ftp->ftps_noffs); 274210198Srpaulo} 275210198Srpaulo 276210198Srpaulo/*ARGSUSED*/ 277210198Srpauloint 278210198Srpaulodt_pid_create_offset_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, 279210198Srpaulo fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, ulong_t off) 280210198Srpaulo{ 281210198Srpaulo if (off & 0x3) 282210198Srpaulo return (DT_PROC_ALIGN); 283210198Srpaulo 284210198Srpaulo ftp->ftps_type = DTFTP_OFFSETS; 285210198Srpaulo ftp->ftps_pc = (uintptr_t)symp->st_value; 286210198Srpaulo ftp->ftps_size = (size_t)symp->st_size; 287210198Srpaulo ftp->ftps_noffs = 1; 288210198Srpaulo ftp->ftps_offs[0] = off; 289210198Srpaulo 290210198Srpaulo if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { 291210198Srpaulo dt_dprintf("fasttrap probe creation ioctl failed: %s\n", 292210198Srpaulo strerror(errno)); 293210198Srpaulo return (dt_set_errno(dtp, errno)); 294210198Srpaulo } 295210198Srpaulo 296210198Srpaulo return (1); 297210198Srpaulo} 298210198Srpaulo 299210198Srpaulo/*ARGSUSED*/ 300210198Srpauloint 301210198Srpaulodt_pid_create_glob_offset_probes(struct ps_prochandle *P, dtrace_hdl_t *dtp, 302210198Srpaulo fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, const char *pattern) 303210198Srpaulo{ 304210198Srpaulo ulong_t i; 305210198Srpaulo 306210198Srpaulo ftp->ftps_type = DTFTP_OFFSETS; 307210198Srpaulo ftp->ftps_pc = (uintptr_t)symp->st_value; 308210198Srpaulo ftp->ftps_size = (size_t)symp->st_size; 309210198Srpaulo ftp->ftps_noffs = 0; 310210198Srpaulo 311210198Srpaulo /* 312210198Srpaulo * If we're matching against everything, just iterate through each 313210198Srpaulo * instruction in the function, otherwise look for matching offset 314210198Srpaulo * names by constructing the string and comparing it against the 315210198Srpaulo * pattern. 316210198Srpaulo */ 317210198Srpaulo if (strcmp("*", pattern) == 0) { 318210198Srpaulo for (i = 0; i < symp->st_size; i += 4) { 319210198Srpaulo ftp->ftps_offs[ftp->ftps_noffs++] = i; 320210198Srpaulo } 321210198Srpaulo } else { 322210198Srpaulo char name[sizeof (i) * 2 + 1]; 323210198Srpaulo 324210198Srpaulo for (i = 0; i < symp->st_size; i += 4) { 325210198Srpaulo (void) sprintf(name, "%lx", i); 326210198Srpaulo if (gmatch(name, pattern)) 327210198Srpaulo ftp->ftps_offs[ftp->ftps_noffs++] = i; 328210198Srpaulo } 329210198Srpaulo } 330210198Srpaulo 331210198Srpaulo if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { 332210198Srpaulo dt_dprintf("fasttrap probe creation ioctl failed: %s\n", 333210198Srpaulo strerror(errno)); 334210198Srpaulo return (dt_set_errno(dtp, errno)); 335210198Srpaulo } 336210198Srpaulo 337210198Srpaulo return (ftp->ftps_noffs); 338210198Srpaulo} 339