1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <fcntl.h> 30 #include <unistd.h> 31 #include <string.h> 32 #include <errno.h> 33 #include <limits.h> 34 35 #include "libproc.h" 36 #include "Pcontrol.h" 37 #include "Pisadep.h" 38 #include "Putil.h" 39 40 #define BLKSIZE (8 * 1024) 41 42 /* 43 * Look for a SYSCALL instruction in the process's address space. 44 */ 45 int 46 Pscantext(struct ps_prochandle *P) 47 { 48 char mapfile[PATH_MAX]; 49 int mapfd; 50 off_t offset; /* offset in text section */ 51 off_t endoff; /* ending offset in text section */ 52 uintptr_t sysaddr; /* address of SYSCALL instruction */ 53 int syspri; /* priority of SYSCALL instruction */ 54 int nbytes; /* number of bytes in buffer */ 55 int n2bytes; /* number of bytes in second buffer */ 56 int nmappings; /* current number of mappings */ 57 prmap_t *pdp; /* pointer to map descriptor */ 58 prmap_t *prbuf; /* buffer for map descriptors */ 59 unsigned nmap; /* number of map descriptors */ 60 uint32_t buf[2 * BLKSIZE / sizeof (uint32_t)]; /* text buffer */ 61 uchar_t *p; 62 63 /* try the most recently-seen syscall address */ 64 syspri = 0; 65 sysaddr = 0; 66 if (P->sysaddr != 0 && 67 (syspri = Pissyscall(P, P->sysaddr))) 68 sysaddr = P->sysaddr; 69 70 /* try the previous instruction */ 71 if (sysaddr == 0 || syspri != 1) 72 syspri = Pissyscall_prev(P, P->status.pr_lwp.pr_reg[R_PC], 73 &sysaddr); 74 75 if (sysaddr != 0 && syspri == 1) { 76 P->sysaddr = sysaddr; 77 return (0); 78 } 79 80 /* open the /proc/<pid>/map file */ 81 (void) snprintf(mapfile, sizeof (mapfile), "%s/%d/map", 82 procfs_path, (int)P->pid); 83 if ((mapfd = open(mapfile, O_RDONLY)) < 0) { 84 dprintf("failed to open %s: %s\n", mapfile, strerror(errno)); 85 return (-1); 86 } 87 88 /* allocate a plausible initial buffer size */ 89 nmap = 50; 90 91 /* read all the map structures, allocating more space as needed */ 92 for (;;) { 93 prbuf = malloc(nmap * sizeof (prmap_t)); 94 if (prbuf == NULL) { 95 dprintf("Pscantext: failed to allocate buffer\n"); 96 (void) close(mapfd); 97 return (-1); 98 } 99 nmappings = pread(mapfd, prbuf, nmap * sizeof (prmap_t), 0L); 100 if (nmappings < 0) { 101 dprintf("Pscantext: failed to read map file: %s\n", 102 strerror(errno)); 103 free(prbuf); 104 (void) close(mapfd); 105 return (-1); 106 } 107 nmappings /= sizeof (prmap_t); 108 if (nmappings < nmap) /* we read them all */ 109 break; 110 /* allocate a bigger buffer */ 111 free(prbuf); 112 nmap *= 2; 113 } 114 (void) close(mapfd); 115 116 /* 117 * Scan each executable mapping looking for a syscall instruction. 118 * In dynamically linked executables, syscall instructions are 119 * typically only found in shared libraries. Because shared libraries 120 * are most often mapped at the top of the address space, we minimize 121 * our expected search time by starting at the last mapping and working 122 * our way down to the first mapping. 123 */ 124 for (pdp = &prbuf[nmappings - 1]; sysaddr == 0 && syspri != 1 && 125 pdp >= prbuf; pdp--) { 126 127 offset = (off_t)pdp->pr_vaddr; /* beginning of text */ 128 endoff = offset + pdp->pr_size; 129 130 /* avoid non-EXEC mappings; avoid the stack and heap */ 131 if ((pdp->pr_mflags&MA_EXEC) == 0 || 132 (endoff > P->status.pr_stkbase && 133 offset < P->status.pr_stkbase + P->status.pr_stksize) || 134 (endoff > P->status.pr_brkbase && 135 offset < P->status.pr_brkbase + P->status.pr_brksize)) 136 continue; 137 138 (void) lseek(P->asfd, (off_t)offset, 0); 139 140 if ((nbytes = read(P->asfd, buf, 2*BLKSIZE)) <= 0) 141 continue; 142 143 if (nbytes < BLKSIZE) 144 n2bytes = 0; 145 else { 146 n2bytes = nbytes - BLKSIZE; 147 nbytes = BLKSIZE; 148 } 149 150 p = (uchar_t *)buf; 151 152 /* search text for a SYSCALL instruction */ 153 while (sysaddr == 0 && syspri != 1 && offset < endoff) { 154 if (nbytes <= 0) { /* shift buffers */ 155 if ((nbytes = n2bytes) <= 0) 156 break; 157 (void) memcpy(buf, 158 &buf[BLKSIZE / sizeof (buf[0])], 159 nbytes); 160 n2bytes = 0; 161 p = (uchar_t *)buf; 162 if (nbytes == BLKSIZE && 163 offset + BLKSIZE < endoff) 164 n2bytes = read(P->asfd, 165 &buf[BLKSIZE / sizeof (buf[0])], 166 BLKSIZE); 167 } 168 169 if (syspri = Pissyscall_text(P, p, nbytes)) 170 sysaddr = offset; 171 172 p += sizeof (instr_t); 173 offset += sizeof (instr_t); 174 nbytes -= sizeof (instr_t); 175 } 176 } 177 178 free(prbuf); 179 180 if ((P->sysaddr = sysaddr) != 0) 181 return (0); 182 else 183 return (-1); 184 } 185