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 2005 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 	free(prbuf);
180 
181 	if ((P->sysaddr = sysaddr) != 0)
182 		return (0);
183 	else
184 		return (-1);
185 }
186