xref: /illumos-gate/usr/src/lib/libproc/common/Pscantext.c (revision fe4627ef755b7c263f91a0e6f07cdca5d7083501)
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