Commit | Line | Data |
---|---|---|
ecc449eb KB |
1 | /*- |
2 | * Copyright (c) 1991 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
ad787160 C |
5 | * This module is believed to contain source code proprietary to AT&T. |
6 | * Use and redistribution is subject to the Berkeley Software License | |
7 | * Agreement and your Software Agreement with AT&T (Western Electric). | |
ecc449eb KB |
8 | */ |
9 | ||
f582ecdc | 10 | #ifndef lint |
ad787160 | 11 | static char sccsid[] = "@(#)access.c 5.4 (Berkeley) 4/4/91"; |
ecc449eb | 12 | #endif /* not lint */ |
f582ecdc KB |
13 | |
14 | /* | |
15 | * Adb: access data in file/process address space. | |
16 | */ | |
17 | ||
18 | #include "defs.h" | |
19 | #include <sys/file.h> | |
20 | #include <sys/ptrace.h> | |
21 | ||
22 | off_t lseek(); | |
23 | ||
24 | /* | |
25 | * Read or write from or to the given address, accessing or altering | |
26 | * only the given byte(s). Return the number of bytes transferred. | |
27 | * Remote (debuggee) addresses are specified as a <space,address> pair. | |
28 | * Neither the remote nor the local address need be aligned. | |
29 | * | |
30 | * If there is a current process, ask the system to do this (via ptrace | |
31 | * [ick]). If debugging the kernel, use vtophys() to map virtual to | |
32 | * physical locations (in a system-dependent manner). Otherwise we | |
33 | * can just read or write the files being debugged directly. | |
34 | */ | |
35 | int | |
36 | adbio(rw, space, rmtaddr, localaddr, cnt) | |
37 | enum rwmode rw; | |
38 | int space; | |
39 | addr_t rmtaddr; | |
40 | caddr_t localaddr; | |
41 | int cnt; | |
42 | { | |
43 | register int ret; | |
44 | register struct map *mp; | |
45 | struct m1 *mm; | |
46 | ||
23f18bc6 KB |
47 | static char *derr = "data address not found"; |
48 | static char *terr = "text address not found"; | |
f582ecdc KB |
49 | #define rwerr() errflag = space & SP_DATA ? derr : terr |
50 | #define within(which) (rmtaddr >= which.b && rmtaddr < which.e) | |
51 | ||
52 | if (space == SP_NONE) { | |
53 | /* The no-space is all zero. */ | |
54 | bzero(localaddr, cnt); | |
55 | return (cnt); | |
56 | } | |
57 | if (pid) { | |
58 | ret = io_ptrace(rw, space, rmtaddr, localaddr, cnt); | |
59 | if (ret != cnt) | |
60 | rwerr(); | |
61 | return (ret); | |
62 | } | |
63 | if (rw == RWMODE_WRITE && !wtflag) | |
64 | error("not in write mode"); | |
65 | mp = space & SP_DATA ? &datmap : &txtmap; | |
66 | if ((space & SP_STAR) == 0 && within(mp->m1)) | |
67 | mm = &mp->m1; | |
68 | else if (within(mp->m2)) | |
69 | mm = &mp->m2; | |
70 | else { | |
71 | rwerr(); | |
72 | return (0); | |
73 | } | |
74 | rmtaddr += mm->f - mm->b; | |
75 | if (kernel && space == SP_DATA) { | |
76 | char *err = NULL; | |
77 | ||
78 | rmtaddr = vtophys(rmtaddr, &err); | |
79 | if (err) { | |
80 | errflag = err; | |
81 | return (0); | |
82 | } | |
83 | } | |
84 | if (lseek(mp->ufd, (off_t)rmtaddr, 0) == -1) { | |
85 | rwerr(); | |
86 | return (0); | |
87 | } | |
88 | if (rw == RWMODE_READ) { | |
89 | ret = read(mp->ufd, localaddr, cnt); | |
90 | /* gratuitously supply extra zeroes at end of file */ | |
91 | if (ret > 0 && ret < cnt) { | |
92 | bzero(localaddr + ret, cnt - ret); | |
93 | ret = cnt; | |
94 | } | |
95 | } else | |
96 | ret = write(mp->ufd, localaddr, cnt); | |
97 | if (ret != cnt) | |
98 | rwerr(); | |
99 | return (ret); | |
100 | #undef rwerr | |
101 | #undef within | |
102 | } | |
103 | ||
104 | /* | |
105 | * Read a single object of length `len' from the core file at the | |
106 | * given offset. Return the length read. (This routine allows vtophys | |
107 | * and kernel crash startup code to read ptes, etc.) | |
108 | */ | |
109 | int | |
110 | readcore(off, addr, len) | |
111 | off_t off; | |
112 | caddr_t addr; | |
113 | int len; | |
114 | { | |
115 | ||
116 | if (lseek(corefile.fd, off, L_SET) == -1) | |
117 | return (-1); | |
118 | return (read(corefile.fd, addr, len)); | |
119 | } | |
120 | ||
121 | /* | |
122 | * THE FOLLOWING IS GROSS. WE SHOULD REPLACE PTRACE WITH SPECIAL | |
123 | * FILES A LA /proc. | |
124 | * | |
125 | * Read or write using ptrace. io_ptrace arranges that the | |
126 | * addresses passed to ptrace are an even multiple of sizeof(int), | |
127 | * and is able to read or write single bytes. | |
128 | * | |
129 | * Since ptrace is so horribly slow, and some commands do repeated | |
130 | * reading of units smaller than an `int', io_ptrace calls cptrace | |
131 | * (cached ptrace) to allow some cacheing. cptrace also converts a | |
132 | * read/write op and a space into a ptrace op, and returns 0 on success | |
133 | * and hence takes a pointer to the value cell rather than the value. | |
f582ecdc | 134 | */ |
3ffa3d77 CT |
135 | struct cache { |
136 | short rop, wop; /* ptrace ops for read and write */ | |
137 | int valid; /* true iff cache entry valid */ | |
138 | int *addr; /* address of cached value */ | |
139 | int val; /* and the value */ | |
140 | }; | |
141 | static struct cache icache = { PT_READ_I, PT_WRITE_I }; | |
142 | static struct cache dcache = { PT_READ_D, PT_WRITE_D }; | |
143 | ||
144 | /* | |
145 | * Invalidate one or both caches. | |
146 | * This is the only function that accepts two spaces simultaneously. | |
147 | */ | |
148 | cacheinval(space) | |
149 | int space; | |
150 | { | |
151 | ||
152 | if (space & SP_INSTR) | |
153 | icache.valid = 0; | |
154 | if (space & SP_DATA) | |
155 | dcache.valid = 0; | |
156 | } | |
157 | ||
f582ecdc KB |
158 | int cachehit, cachemiss; /* statistics */ |
159 | ||
160 | static int | |
161 | cptrace(rw, space, p, addr, val) | |
162 | enum rwmode rw; | |
163 | int space, p, *addr, *val; | |
164 | { | |
3ffa3d77 | 165 | register struct cache *c = space & SP_DATA ? &dcache : &icache; |
f582ecdc KB |
166 | int v; |
167 | ||
f582ecdc | 168 | if (rw == RWMODE_READ) { |
3ffa3d77 | 169 | if (c->valid && c->addr == addr) { |
f582ecdc KB |
170 | cachehit++; |
171 | *val = c->val; | |
172 | return (0); | |
173 | } | |
174 | cachemiss++; | |
175 | errno = 0; | |
176 | if ((v = ptrace(c->rop, p, addr, 0)) == -1 && errno) | |
177 | return (-1); | |
178 | *val = v; | |
179 | } else { | |
3ffa3d77 | 180 | c->valid = 0; /* paranoia */ |
f582ecdc KB |
181 | errno = 0; |
182 | if (ptrace(c->wop, p, addr, v = *val) == -1 && errno) | |
183 | return (-1); | |
184 | } | |
3ffa3d77 | 185 | c->valid = 1; |
f582ecdc KB |
186 | c->addr = addr; |
187 | c->val = v; | |
188 | return (0); | |
189 | } | |
190 | ||
191 | int | |
192 | io_ptrace(rw, space, rmtaddr, localaddr, cnt) | |
193 | register enum rwmode rw; | |
194 | register int space; | |
195 | addr_t rmtaddr; | |
196 | register caddr_t localaddr; | |
197 | register int cnt; | |
198 | { | |
199 | register addr_t addr; | |
200 | register int nbytes, ret = 0, off; | |
201 | int tmp; | |
202 | ||
203 | /* | |
204 | * Start by aligning rmtaddr; set nbytes to the number of bytes of | |
205 | * useful data we shall obtain. | |
206 | */ | |
207 | off = rmtaddr % sizeof(int); /* addr_t is unsigned */ | |
208 | addr = rmtaddr - off; | |
209 | nbytes = sizeof(int) - off; | |
210 | while (cnt != 0) { | |
211 | if (cnt < nbytes) | |
212 | nbytes = cnt; | |
213 | if (rw == RWMODE_READ) { | |
214 | if (cptrace(rw, space, pid, (int *)addr, &tmp)) | |
215 | return (ret); | |
216 | bcopy((caddr_t)&tmp + off, localaddr, nbytes); | |
217 | } else { | |
218 | if (nbytes < sizeof(int) && | |
219 | cptrace(RWMODE_READ, space, pid, (int *)addr, &tmp)) | |
220 | return (ret); | |
221 | bcopy(localaddr, (caddr_t)&tmp + off, nbytes); | |
222 | if (cptrace(rw, space, pid, (int *)addr, &tmp)) | |
223 | return (ret); | |
224 | } | |
225 | addr += sizeof(int); | |
226 | localaddr += nbytes; | |
227 | ret += nbytes; | |
228 | cnt -= nbytes; | |
229 | /* | |
230 | * For the rest of the loop, the offset is 0 and we can | |
231 | * use all the bytes obtained. | |
232 | */ | |
233 | off = 0; | |
234 | nbytes = sizeof(int); | |
235 | } | |
236 | return (ret); | |
237 | } |