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
Pscantext(struct ps_prochandle * P)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