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