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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <fcntl.h> 33 #include <unistd.h> 34 #include <string.h> 35 #include <errno.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[100]; 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) sprintf(mapfile, "/proc/%d/map", (int)P->pid); 84 if ((mapfd = open(mapfile, O_RDONLY)) < 0) { 85 dprintf("failed to open %s: %s\n", mapfile, strerror(errno)); 86 return (-1); 87 } 88 89 /* allocate a plausible initial buffer size */ 90 nmap = 50; 91 92 /* read all the map structures, allocating more space as needed */ 93 for (;;) { 94 prbuf = malloc(nmap * sizeof (prmap_t)); 95 if (prbuf == NULL) { 96 dprintf("Pscantext: failed to allocate buffer\n"); 97 (void) close(mapfd); 98 return (-1); 99 } 100 nmappings = pread(mapfd, prbuf, nmap * sizeof (prmap_t), 0L); 101 if (nmappings < 0) { 102 dprintf("Pscantext: failed to read map file: %s\n", 103 strerror(errno)); 104 free(prbuf); 105 (void) close(mapfd); 106 return (-1); 107 } 108 nmappings /= sizeof (prmap_t); 109 if (nmappings < nmap) /* we read them all */ 110 break; 111 /* allocate a bigger buffer */ 112 free(prbuf); 113 nmap *= 2; 114 } 115 (void) close(mapfd); 116 117 /* 118 * Scan each executable mapping looking for a syscall instruction. 119 * In dynamically linked executables, syscall instructions are 120 * typically only found in shared libraries. Because shared libraries 121 * are most often mapped at the top of the address space, we minimize 122 * our expected search time by starting at the last mapping and working 123 * our way down to the first mapping. 124 */ 125 for (pdp = &prbuf[nmappings - 1]; sysaddr == 0 && syspri != 1 && 126 pdp >= prbuf; pdp--) { 127 128 offset = (off_t)pdp->pr_vaddr; /* beginning of text */ 129 endoff = offset + pdp->pr_size; 130 131 /* avoid non-EXEC mappings; avoid the stack and heap */ 132 if ((pdp->pr_mflags&MA_EXEC) == 0 || 133 (endoff > P->status.pr_stkbase && 134 offset < P->status.pr_stkbase + P->status.pr_stksize) || 135 (endoff > P->status.pr_brkbase && 136 offset < P->status.pr_brkbase + P->status.pr_brksize)) 137 continue; 138 139 (void) lseek(P->asfd, (off_t)offset, 0); 140 141 if ((nbytes = read(P->asfd, buf, 2*BLKSIZE)) <= 0) 142 continue; 143 144 if (nbytes < BLKSIZE) 145 n2bytes = 0; 146 else { 147 n2bytes = nbytes - BLKSIZE; 148 nbytes = BLKSIZE; 149 } 150 151 p = (uchar_t *)buf; 152 153 /* search text for a SYSCALL instruction */ 154 while (sysaddr == 0 && syspri != 1 && offset < endoff) { 155 if (nbytes <= 0) { /* shift buffers */ 156 if ((nbytes = n2bytes) <= 0) 157 break; 158 (void) memcpy(buf, 159 &buf[BLKSIZE / sizeof (buf[0])], 160 nbytes); 161 n2bytes = 0; 162 p = (uchar_t *)buf; 163 if (nbytes == BLKSIZE && 164 offset + BLKSIZE < endoff) 165 n2bytes = read(P->asfd, 166 &buf[BLKSIZE / sizeof (buf[0])], 167 BLKSIZE); 168 } 169 170 if (syspri = Pissyscall_text(P, p, nbytes)) 171 sysaddr = offset; 172 173 p += sizeof (instr_t); 174 offset += sizeof (instr_t); 175 nbytes -= sizeof (instr_t); 176 } 177 } 178 179 if ((P->sysaddr = sysaddr) != 0) 180 return (0); 181 else 182 return (-1); 183 } 184