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 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <fcntl.h> 32 #include <unistd.h> 33 #include <string.h> 34 #include <errno.h> 35 #include <limits.h> 36 37 #include "libproc.h" 38 #include "Pcontrol.h" 39 #include "Pisadep.h" 40 #include "Putil.h" 41 42 #define BLKSIZE (8 * 1024) 43 44 /* 45 * Look for a SYSCALL instruction in the process's address space. 46 */ 47 int 48 Pscantext(struct ps_prochandle *P) 49 { 50 char mapfile[PATH_MAX]; 51 int mapfd; 52 off_t offset; /* offset in text section */ 53 off_t endoff; /* ending offset in text section */ 54 uintptr_t sysaddr; /* address of SYSCALL instruction */ 55 int syspri; /* priority of SYSCALL instruction */ 56 int nbytes; /* number of bytes in buffer */ 57 int n2bytes; /* number of bytes in second buffer */ 58 int nmappings; /* current number of mappings */ 59 prmap_t *pdp; /* pointer to map descriptor */ 60 prmap_t *prbuf; /* buffer for map descriptors */ 61 unsigned nmap; /* number of map descriptors */ 62 uint32_t buf[2 * BLKSIZE / sizeof (uint32_t)]; /* text buffer */ 63 uchar_t *p; 64 65 /* try the most recently-seen syscall address */ 66 syspri = 0; 67 sysaddr = 0; 68 if (P->sysaddr != 0 && 69 (syspri = Pissyscall(P, P->sysaddr))) 70 sysaddr = P->sysaddr; 71 72 /* try the previous instruction */ 73 if (sysaddr == 0 || syspri != 1) 74 syspri = Pissyscall_prev(P, P->status.pr_lwp.pr_reg[R_PC], 75 &sysaddr); 76 77 if (sysaddr != 0 && syspri == 1) { 78 P->sysaddr = sysaddr; 79 return (0); 80 } 81 82 /* open the /proc/<pid>/map file */ 83 (void) snprintf(mapfile, sizeof (mapfile), "%s/%d/map", 84 procfs_path, (int)P->pid); 85 if ((mapfd = open(mapfile, O_RDONLY)) < 0) { 86 dprintf("failed to open %s: %s\n", mapfile, strerror(errno)); 87 return (-1); 88 } 89 90 /* allocate a plausible initial buffer size */ 91 nmap = 50; 92 93 /* read all the map structures, allocating more space as needed */ 94 for (;;) { 95 prbuf = malloc(nmap * sizeof (prmap_t)); 96 if (prbuf == NULL) { 97 dprintf("Pscantext: failed to allocate buffer\n"); 98 (void) close(mapfd); 99 return (-1); 100 } 101 nmappings = pread(mapfd, prbuf, nmap * sizeof (prmap_t), 0L); 102 if (nmappings < 0) { 103 dprintf("Pscantext: failed to read map file: %s\n", 104 strerror(errno)); 105 free(prbuf); 106 (void) close(mapfd); 107 return (-1); 108 } 109 nmappings /= sizeof (prmap_t); 110 if (nmappings < nmap) /* we read them all */ 111 break; 112 /* allocate a bigger buffer */ 113 free(prbuf); 114 nmap *= 2; 115 } 116 (void) close(mapfd); 117 118 /* 119 * Scan each executable mapping looking for a syscall instruction. 120 * In dynamically linked executables, syscall instructions are 121 * typically only found in shared libraries. Because shared libraries 122 * are most often mapped at the top of the address space, we minimize 123 * our expected search time by starting at the last mapping and working 124 * our way down to the first mapping. 125 */ 126 for (pdp = &prbuf[nmappings - 1]; sysaddr == 0 && syspri != 1 && 127 pdp >= prbuf; pdp--) { 128 129 offset = (off_t)pdp->pr_vaddr; /* beginning of text */ 130 endoff = offset + pdp->pr_size; 131 132 /* avoid non-EXEC mappings; avoid the stack and heap */ 133 if ((pdp->pr_mflags&MA_EXEC) == 0 || 134 (endoff > P->status.pr_stkbase && 135 offset < P->status.pr_stkbase + P->status.pr_stksize) || 136 (endoff > P->status.pr_brkbase && 137 offset < P->status.pr_brkbase + P->status.pr_brksize)) 138 continue; 139 140 (void) lseek(P->asfd, (off_t)offset, 0); 141 142 if ((nbytes = read(P->asfd, buf, 2*BLKSIZE)) <= 0) 143 continue; 144 145 if (nbytes < BLKSIZE) 146 n2bytes = 0; 147 else { 148 n2bytes = nbytes - BLKSIZE; 149 nbytes = BLKSIZE; 150 } 151 152 p = (uchar_t *)buf; 153 154 /* search text for a SYSCALL instruction */ 155 while (sysaddr == 0 && syspri != 1 && offset < endoff) { 156 if (nbytes <= 0) { /* shift buffers */ 157 if ((nbytes = n2bytes) <= 0) 158 break; 159 (void) memcpy(buf, 160 &buf[BLKSIZE / sizeof (buf[0])], 161 nbytes); 162 n2bytes = 0; 163 p = (uchar_t *)buf; 164 if (nbytes == BLKSIZE && 165 offset + BLKSIZE < endoff) 166 n2bytes = read(P->asfd, 167 &buf[BLKSIZE / sizeof (buf[0])], 168 BLKSIZE); 169 } 170 171 if (syspri = Pissyscall_text(P, p, nbytes)) 172 sysaddr = offset; 173 174 p += sizeof (instr_t); 175 offset += sizeof (instr_t); 176 nbytes -= sizeof (instr_t); 177 } 178 } 179 180 free(prbuf); 181 182 if ((P->sysaddr = sysaddr) != 0) 183 return (0); 184 else 185 return (-1); 186 } 187