|
1 // Copyright (c) 2007, Google Inc. |
|
2 // All rights reserved. |
|
3 // |
|
4 // Redistribution and use in source and binary forms, with or without |
|
5 // modification, are permitted provided that the following conditions are |
|
6 // met: |
|
7 // |
|
8 // * Redistributions of source code must retain the above copyright |
|
9 // notice, this list of conditions and the following disclaimer. |
|
10 // * Redistributions in binary form must reproduce the above |
|
11 // copyright notice, this list of conditions and the following disclaimer |
|
12 // in the documentation and/or other materials provided with the |
|
13 // distribution. |
|
14 // * Neither the name of Google Inc. nor the names of its |
|
15 // contributors may be used to endorse or promote products derived from |
|
16 // this software without specific prior written permission. |
|
17 // |
|
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
29 |
|
30 // Author: Alfred Peng |
|
31 |
|
32 #include <dirent.h> |
|
33 #include <elf.h> |
|
34 #include <errno.h> |
|
35 #include <fcntl.h> |
|
36 #include <limits.h> |
|
37 #include <sys/frame.h> |
|
38 #include <sys/stat.h> |
|
39 #include <sys/types.h> |
|
40 #include <sys/wait.h> |
|
41 #include <unistd.h> |
|
42 |
|
43 #include <algorithm> |
|
44 #include <cassert> |
|
45 #include <cstdio> |
|
46 #include <cstdlib> |
|
47 #include <functional> |
|
48 |
|
49 #include "client/solaris/handler/solaris_lwp.h" |
|
50 #include "common/solaris/message_output.h" |
|
51 |
|
52 using namespace google_breakpad; |
|
53 |
|
54 // This unamed namespace contains helper function. |
|
55 namespace { |
|
56 |
|
57 uintptr_t stack_base_address = 0; |
|
58 static const int HEADER_MAX = 2000; |
|
59 static const int MAP_MAX = 1000; |
|
60 |
|
61 // Context information for the callbacks when validating address by listing |
|
62 // modules. |
|
63 struct AddressValidatingContext { |
|
64 uintptr_t address; |
|
65 bool is_mapped; |
|
66 |
|
67 AddressValidatingContext() : address(0UL), is_mapped(false) { |
|
68 } |
|
69 }; |
|
70 |
|
71 // Convert from string to int. |
|
72 static bool LocalAtoi(char *s, int *r) { |
|
73 assert(s != NULL); |
|
74 assert(r != NULL); |
|
75 char *endptr = NULL; |
|
76 int ret = strtol(s, &endptr, 10); |
|
77 if (endptr == s) |
|
78 return false; |
|
79 *r = ret; |
|
80 return true; |
|
81 } |
|
82 |
|
83 // Callback invoked for each mapped module. |
|
84 // It uses the module's adderss range to validate the address. |
|
85 static bool AddressNotInModuleCallback(const ModuleInfo &module_info, |
|
86 void *context) { |
|
87 AddressValidatingContext *addr = |
|
88 reinterpret_cast<AddressValidatingContext *>(context); |
|
89 if (addr->is_mapped = ((module_info.start_addr > 0) && |
|
90 (addr->address >= module_info.start_addr) && |
|
91 (addr->address <= module_info.start_addr + |
|
92 module_info.size))) { |
|
93 stack_base_address = module_info.start_addr + module_info.size; |
|
94 } |
|
95 |
|
96 return !addr->is_mapped; |
|
97 } |
|
98 |
|
99 static int IterateLwpAll(int pid, |
|
100 CallbackParam<LwpidCallback> *callback_param) { |
|
101 char lwp_path[40]; |
|
102 DIR *dir; |
|
103 int count = 0; |
|
104 |
|
105 snprintf(lwp_path, sizeof (lwp_path), "/proc/%d/lwp", (int)pid); |
|
106 if ((dir = opendir(lwp_path)) == NULL) |
|
107 return -1; |
|
108 |
|
109 struct dirent *entry = NULL; |
|
110 while ((entry = readdir(dir)) != NULL) { |
|
111 if ((strcmp(entry->d_name, ".") != 0) && |
|
112 (strcmp(entry->d_name, "..") != 0)) { |
|
113 int lwpid = 0; |
|
114 int last_pid = 0; |
|
115 if (LocalAtoi(entry->d_name, &lwpid) && last_pid != lwpid) { |
|
116 last_pid = lwpid; |
|
117 ++count; |
|
118 if (callback_param && |
|
119 !(callback_param->call_back)(lwpid, callback_param->context)) { |
|
120 break; |
|
121 } |
|
122 } |
|
123 } |
|
124 } |
|
125 |
|
126 closedir(dir); |
|
127 return count; |
|
128 } |
|
129 |
|
130 #if defined(__i386) && !defined(NO_FRAME_POINTER) |
|
131 void *GetNextFrame(void **last_ebp) { |
|
132 void *sp = *last_ebp; |
|
133 if ((unsigned long)sp == (unsigned long)last_ebp) |
|
134 return NULL; |
|
135 if ((unsigned long)sp & (sizeof(void *) - 1)) |
|
136 return NULL; |
|
137 if ((unsigned long)sp - (unsigned long)last_ebp > 100000) |
|
138 return NULL; |
|
139 return sp; |
|
140 } |
|
141 #elif defined(__sparc) |
|
142 void *GetNextFrame(void *last_ebp) { |
|
143 return reinterpret_cast<struct frame *>(last_ebp)->fr_savfp; |
|
144 } |
|
145 #else |
|
146 void *GetNextFrame(void **last_ebp) { |
|
147 return reinterpret_cast<void*>(last_ebp); |
|
148 } |
|
149 #endif |
|
150 |
|
151 |
|
152 class AutoCloser { |
|
153 public: |
|
154 AutoCloser(int fd) : fd_(fd) {} |
|
155 ~AutoCloser() { if (fd_) close(fd_); } |
|
156 private: |
|
157 int fd_; |
|
158 }; |
|
159 |
|
160 // Control the execution of the lwp. |
|
161 // Suspend/Resume lwp based on the value of context. |
|
162 static bool ControlLwp(int lwpid, void *context) { |
|
163 // The current thread is the one to handle the crash. Ignore it. |
|
164 if (lwpid != pthread_self()) { |
|
165 int ctlfd; |
|
166 char procname[PATH_MAX]; |
|
167 bool suspend = *(bool *)context; |
|
168 |
|
169 // Open the /proc/$pid/lwp/$lwpid/lwpctl files |
|
170 snprintf(procname, sizeof (procname), "/proc/self/lwp/%d/lwpctl", lwpid); |
|
171 |
|
172 if ((ctlfd = open(procname, O_WRONLY|O_EXCL)) < 0) { |
|
173 print_message2(2, "failed to open %s in ControlLwp\n", procname); |
|
174 return false; |
|
175 } |
|
176 |
|
177 AutoCloser autocloser(ctlfd); |
|
178 |
|
179 long ctl[2]; |
|
180 ctl[0] = suspend ? PCSTOP : PCRUN; |
|
181 ctl[1] = 0; |
|
182 if (write(ctlfd, ctl, sizeof (ctl)) != sizeof (ctl)) { |
|
183 print_message2(2, "failed in lwp %d\n", lwpid); |
|
184 return false; |
|
185 } |
|
186 } |
|
187 |
|
188 return true; |
|
189 } |
|
190 |
|
191 /* |
|
192 * Utility function to read the contents of a file that contains a |
|
193 * prheader_t at the start (/proc/$pid/lstatus or /proc/$pid/lpsinfo). |
|
194 * Return true on success. |
|
195 */ |
|
196 static bool read_lfile(int pid, const char *lname, prheader_t *lhp) { |
|
197 char lpath[PATH_MAX]; |
|
198 struct stat statb; |
|
199 int fd; |
|
200 size_t size; |
|
201 |
|
202 snprintf(lpath, sizeof (lpath), "/proc/%d/%s", pid, lname); |
|
203 if ((fd = open(lpath, O_RDONLY)) < 0) { |
|
204 print_message2(2, "failed to open %s in read_lfile\n", lpath); |
|
205 return false; |
|
206 } |
|
207 |
|
208 AutoCloser autocloser(fd); |
|
209 |
|
210 if (fstat(fd, &statb) != 0) |
|
211 return false; |
|
212 |
|
213 size = statb.st_size; |
|
214 if ((size / sizeof (prheader_t)) + 32 > HEADER_MAX) { |
|
215 print_message1(2, "map size overflow\n"); |
|
216 return false; |
|
217 } |
|
218 |
|
219 if (pread(fd, lhp, size, 0) <= sizeof (prheader_t)) |
|
220 return false; |
|
221 |
|
222 return true; |
|
223 } |
|
224 |
|
225 } // namespace |
|
226 |
|
227 namespace google_breakpad { |
|
228 |
|
229 SolarisLwp::SolarisLwp(int pid) : pid_(pid) { |
|
230 } |
|
231 |
|
232 SolarisLwp::~SolarisLwp() { |
|
233 } |
|
234 |
|
235 int SolarisLwp::ControlAllLwps(bool suspend) { |
|
236 CallbackParam<LwpidCallback> callback_param(ControlLwp, &suspend); |
|
237 return IterateLwpAll(pid_, &callback_param); |
|
238 } |
|
239 |
|
240 int SolarisLwp::GetLwpCount() const { |
|
241 return IterateLwpAll(pid_, NULL); |
|
242 } |
|
243 |
|
244 int SolarisLwp::Lwp_iter_all(int pid, |
|
245 CallbackParam<LwpCallback> *callback_param) const { |
|
246 lwpstatus_t *Lsp; |
|
247 lwpstatus_t *sp; |
|
248 prheader_t lphp[HEADER_MAX]; |
|
249 prheader_t lhp[HEADER_MAX]; |
|
250 prheader_t *Lphp = lphp; |
|
251 prheader_t *Lhp = lhp; |
|
252 lwpsinfo_t *Lpsp; |
|
253 long nstat; |
|
254 long ninfo; |
|
255 int rv = 0; |
|
256 |
|
257 /* |
|
258 * The /proc/pid/lstatus file has the array of lwpstatus_t's and the |
|
259 * /proc/pid/lpsinfo file has the array of lwpsinfo_t's. |
|
260 */ |
|
261 if (read_lfile(pid, "lstatus", Lhp) == NULL) |
|
262 return -1; |
|
263 if (read_lfile(pid, "lpsinfo", Lphp) == NULL) { |
|
264 return -1; |
|
265 } |
|
266 |
|
267 Lsp = (lwpstatus_t *)(uintptr_t)(Lhp + 1); |
|
268 Lpsp = (lwpsinfo_t *)(uintptr_t)(Lphp + 1); |
|
269 |
|
270 for (ninfo = Lphp->pr_nent; ninfo != 0; --ninfo) { |
|
271 if (Lpsp->pr_sname != 'Z') { |
|
272 sp = Lsp; |
|
273 Lsp = (lwpstatus_t *)((uintptr_t)Lsp + Lhp->pr_entsize); |
|
274 } else { |
|
275 sp = NULL; |
|
276 } |
|
277 if (callback_param && |
|
278 !(callback_param->call_back)(sp, callback_param->context)) |
|
279 break; |
|
280 ++rv; |
|
281 Lpsp = (lwpsinfo_t *)((uintptr_t)Lpsp + Lphp->pr_entsize); |
|
282 } |
|
283 |
|
284 return rv; |
|
285 } |
|
286 |
|
287 uintptr_t SolarisLwp::GetLwpStackBottom(uintptr_t current_esp) const { |
|
288 AddressValidatingContext addr; |
|
289 addr.address = current_esp; |
|
290 CallbackParam<ModuleCallback> callback_param(AddressNotInModuleCallback, |
|
291 &addr); |
|
292 ListModules(&callback_param); |
|
293 return stack_base_address; |
|
294 } |
|
295 |
|
296 int SolarisLwp::GetModuleCount() const { |
|
297 return ListModules(NULL); |
|
298 } |
|
299 |
|
300 int SolarisLwp::ListModules( |
|
301 CallbackParam<ModuleCallback> *callback_param) const { |
|
302 const char *maps_path = "/proc/self/map"; |
|
303 struct stat status; |
|
304 int fd = 0, num; |
|
305 prmap_t map_array[MAP_MAX]; |
|
306 prmap_t *maps = map_array; |
|
307 size_t size; |
|
308 |
|
309 if ((fd = open(maps_path, O_RDONLY)) == -1) { |
|
310 print_message2(2, "failed to open %s in ListModules\n", maps_path); |
|
311 return -1; |
|
312 } |
|
313 |
|
314 AutoCloser autocloser(fd); |
|
315 |
|
316 if (fstat(fd, &status)) |
|
317 return -1; |
|
318 |
|
319 /* |
|
320 * Determine number of mappings, this value must be |
|
321 * larger than the actual module count |
|
322 */ |
|
323 size = status.st_size; |
|
324 if ((num = (int)(size / sizeof (prmap_t))) > MAP_MAX) { |
|
325 print_message1(2, "map size overflow\n"); |
|
326 return -1; |
|
327 } |
|
328 |
|
329 if (read(fd, (void *)maps, size) < 0) { |
|
330 print_message2(2, "failed to read %d\n", fd); |
|
331 return -1; |
|
332 } |
|
333 |
|
334 prmap_t *_maps; |
|
335 int _num; |
|
336 int module_count = 0; |
|
337 |
|
338 /* |
|
339 * Scan each mapping - note it is assummed that the mappings are |
|
340 * presented in order. We fill holes between mappings. On intel |
|
341 * the last mapping is usually the data segment of ld.so.1, after |
|
342 * this comes a red zone into which non-fixed mapping won't get |
|
343 * place. Thus we can simply bail from the loop after seeing the |
|
344 * last mapping. |
|
345 */ |
|
346 for (_num = 0, _maps = maps; _num < num; ++_num, ++_maps) { |
|
347 ModuleInfo module; |
|
348 char *name = _maps->pr_mapname; |
|
349 |
|
350 memset(&module, 0, sizeof (module)); |
|
351 module.start_addr = _maps->pr_vaddr; |
|
352 module.size = _maps->pr_size; |
|
353 if (strlen(name) > 0) { |
|
354 int objectfd = 0; |
|
355 char path[PATH_MAX]; |
|
356 char buf[SELFMAG]; |
|
357 |
|
358 snprintf(path, sizeof (path), "/proc/self/object/%s", name); |
|
359 if ((objectfd = open(path, O_RDONLY)) < 0) { |
|
360 print_message1(2, "can't open module file\n"); |
|
361 continue; |
|
362 } |
|
363 |
|
364 AutoCloser autocloser(objectfd); |
|
365 |
|
366 if (read(objectfd, buf, SELFMAG) != SELFMAG) { |
|
367 print_message1(2, "can't read module file\n"); |
|
368 continue; |
|
369 } |
|
370 if (buf[0] != ELFMAG0 || buf[1] != ELFMAG1 || |
|
371 buf[2] != ELFMAG2 || buf[3] != ELFMAG3) { |
|
372 continue; |
|
373 } |
|
374 |
|
375 strncpy(module.name, name, sizeof (module.name) - 1); |
|
376 ++module_count; |
|
377 } |
|
378 if (callback_param && |
|
379 (!callback_param->call_back(module, callback_param->context))) { |
|
380 break; |
|
381 } |
|
382 } |
|
383 |
|
384 return module_count; |
|
385 } |
|
386 |
|
387 // Check if the address is a valid virtual address. |
|
388 // If the address is in any of the mapped modules, we take it as valid. |
|
389 // Otherwise it is invalid. |
|
390 bool SolarisLwp::IsAddressMapped(uintptr_t address) const { |
|
391 AddressValidatingContext addr; |
|
392 addr.address = address; |
|
393 CallbackParam<ModuleCallback> callback_param(AddressNotInModuleCallback, |
|
394 &addr); |
|
395 ListModules(&callback_param); |
|
396 return addr.is_mapped; |
|
397 } |
|
398 |
|
399 // We're looking for a ucontext_t as the second parameter |
|
400 // to a signal handler function call. Luckily, the ucontext_t |
|
401 // has an ebp(fp on SPARC) member which should match the ebp(fp) |
|
402 // pointed to by the ebp(fp) of the signal handler frame. |
|
403 // The Solaris stack looks like this: |
|
404 // http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libproc/common/Pstack.c#81 |
|
405 bool SolarisLwp::FindSigContext(uintptr_t sighandler_ebp, |
|
406 ucontext_t **sig_ctx) { |
|
407 uintptr_t previous_ebp; |
|
408 uintptr_t sig_ebp; |
|
409 const int MAX_STACK_DEPTH = 50; |
|
410 int depth_counter = 0; |
|
411 |
|
412 do { |
|
413 #if TARGET_CPU_SPARC |
|
414 previous_ebp = reinterpret_cast<uintptr_t>(GetNextFrame( |
|
415 reinterpret_cast<void*>(sighandler_ebp))); |
|
416 *sig_ctx = reinterpret_cast<ucontext_t*>(sighandler_ebp + sizeof (struct frame)); |
|
417 uintptr_t sig_esp = (*sig_ctx)->uc_mcontext.gregs[REG_O6]; |
|
418 if (sig_esp < previous_ebp && sig_esp > sighandler_ebp) |
|
419 sig_ebp = (uintptr_t)(((struct frame *)sig_esp)->fr_savfp); |
|
420 |
|
421 #elif TARGET_CPU_X86 |
|
422 previous_ebp = reinterpret_cast<uintptr_t>(GetNextFrame( |
|
423 reinterpret_cast<void**>(sighandler_ebp))); |
|
424 *sig_ctx = reinterpret_cast<ucontext_t*>(sighandler_ebp + sizeof (struct frame) + |
|
425 3 * sizeof(uintptr_t)); |
|
426 sig_ebp = (*sig_ctx)->uc_mcontext.gregs[EBP]; |
|
427 #endif |
|
428 sighandler_ebp = previous_ebp; |
|
429 depth_counter++; |
|
430 } while(previous_ebp != sig_ebp && sighandler_ebp != 0 && |
|
431 IsAddressMapped(sighandler_ebp) && depth_counter < MAX_STACK_DEPTH); |
|
432 |
|
433 return previous_ebp == sig_ebp && previous_ebp != 0; |
|
434 } |
|
435 |
|
436 } // namespace google_breakpad |