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