Commit | Line | Data |
---|---|---|
8b6a428f SS |
1 | /* |
2 | * Copyright (c) 2004 Marcel Moolenaar | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * | |
9 | * 1. Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions and the following disclaimer. | |
11 | * 2. Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * | |
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
25 | * | |
26 | * $FreeBSD: src/gnu/usr.bin/gdb/kgdb/main.c,v 1.11 2006/01/04 23:17:52 kan Exp $ | |
0c3d4888 | 27 | * $DragonFly: src/gnu/usr.bin/gdb/kgdb/kgdb.c,v 1.3 2008/01/14 21:36:38 corecode Exp $ |
8b6a428f SS |
28 | */ |
29 | ||
30 | #include <sys/cdefs.h> | |
31 | ||
32 | #include <sys/param.h> | |
33 | #include <sys/stat.h> | |
34 | #include <sys/types.h> | |
35 | #include <sys/ioctl.h> | |
36 | #include <sys/resource.h> | |
37 | #include <sys/select.h> | |
38 | #include <sys/time.h> | |
39 | #include <sys/wait.h> | |
c499d304 | 40 | #include <sys/msgbuf.h> |
8b6a428f SS |
41 | #include <errno.h> |
42 | #include <err.h> | |
43 | #include <fcntl.h> | |
44 | #include <inttypes.h> | |
45 | #include <kvm.h> | |
46 | #include <limits.h> | |
47 | #include <paths.h> | |
48 | #include <stdio.h> | |
49 | #include <stdlib.h> | |
50 | #include <string.h> | |
51 | #include <unistd.h> | |
52 | #include <linker_set.h> | |
53 | ||
54 | /* libgdb stuff. */ | |
55 | #include <defs.h> | |
56 | #include <frame.h> | |
57 | #include <frame-unwind.h> | |
58 | #include <inferior.h> | |
59 | #include <interps.h> | |
60 | #include <cli-out.h> | |
61 | #include <main.h> | |
62 | #include <target.h> | |
63 | #include <top.h> | |
64 | #include <bfd.h> | |
65 | #include <gdbcore.h> | |
66 | #include <target.h> | |
67 | ||
68 | extern void symbol_file_add_main (char *args, int from_tty); | |
69 | ||
70 | #include "kgdb.h" | |
71 | ||
72 | kvm_t *kvm; | |
73 | static char kvm_err[_POSIX2_LINE_MAX]; | |
74 | ||
75 | static int dumpnr; | |
76 | static int verbose; | |
77 | ||
78 | static char crashdir[PATH_MAX]; | |
79 | static char *kernel; | |
80 | static char *remote; | |
81 | static char *vmcore; | |
82 | ||
83 | static void (*kgdb_new_objfile_chain)(struct objfile * objfile); | |
84 | ||
c499d304 SS |
85 | uintptr_t |
86 | lookup(const char *sym) | |
87 | { | |
88 | struct nlist nl[2]; | |
89 | ||
90 | nl[0].n_name = (char *)(uintptr_t)sym; | |
91 | nl[1].n_name = NULL; | |
92 | if (kvm_nlist(kvm, nl) != 0) { | |
93 | warnx("kvm_nlist(%s): %s", sym, kvm_geterr(kvm)); | |
94 | return (0); | |
95 | } | |
96 | return (nl[0].n_value); | |
97 | } | |
98 | ||
8b6a428f SS |
99 | static void |
100 | kgdb_atexit(void) | |
101 | { | |
102 | if (kvm != NULL) | |
103 | kvm_close(kvm); | |
104 | } | |
105 | ||
106 | static void | |
107 | usage(void) | |
108 | { | |
109 | ||
110 | fprintf(stderr, | |
111 | "usage: %s [-afqv] [-d crashdir] [-c core | -n dumpnr | -r device]\n" | |
112 | "\t[kernel [core]]\n", getprogname()); | |
113 | exit(1); | |
114 | } | |
115 | ||
116 | static void | |
117 | kernel_from_dumpnr(int nr) | |
118 | { | |
119 | char path[PATH_MAX]; | |
120 | FILE *info; | |
121 | char *s; | |
122 | struct stat st; | |
123 | int l; | |
124 | ||
125 | /* | |
126 | * If there's a kernel image right here in the crash directory, then | |
127 | * use it. The kernel image is either called kernel.<nr> or is in a | |
128 | * subdirectory kernel.<nr> and called kernel. The latter allows us | |
129 | * to collect the modules in the same place. | |
130 | */ | |
131 | snprintf(path, sizeof(path), "%s/kernel.%d", crashdir, nr); | |
132 | if (stat(path, &st) == 0) { | |
133 | if (S_ISREG(st.st_mode)) { | |
134 | kernel = strdup(path); | |
135 | return; | |
136 | } | |
137 | if (S_ISDIR(st.st_mode)) { | |
138 | snprintf(path, sizeof(path), "%s/kernel.%d/kernel", | |
139 | crashdir, nr); | |
140 | if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) { | |
141 | kernel = strdup(path); | |
142 | return; | |
143 | } | |
144 | } | |
145 | } | |
146 | ||
147 | /* | |
148 | * No kernel image here. Parse the dump header. The kernel object | |
149 | * directory can be found there and we probably have the kernel | |
150 | * image still in it. The object directory may also have a kernel | |
151 | * with debugging info (called kernel.debug). If we have a debug | |
152 | * kernel, use it. | |
153 | */ | |
154 | snprintf(path, sizeof(path), "%s/info.%d", crashdir, nr); | |
155 | info = fopen(path, "r"); | |
156 | if (info == NULL) { | |
157 | warn(path); | |
158 | return; | |
159 | } | |
160 | while (fgets(path, sizeof(path), info) != NULL) { | |
161 | l = strlen(path); | |
162 | if (l > 0 && path[l - 1] == '\n') | |
163 | path[--l] = '\0'; | |
164 | if (strncmp(path, " ", 4) == 0) { | |
165 | s = strchr(path, ':'); | |
166 | s = (s == NULL) ? path + 4 : s + 1; | |
167 | l = snprintf(path, sizeof(path), "%s/kernel.debug", s); | |
168 | if (stat(path, &st) == -1 || !S_ISREG(st.st_mode)) { | |
169 | path[l - 6] = '\0'; | |
170 | if (stat(path, &st) == -1 || | |
171 | !S_ISREG(st.st_mode)) | |
172 | break; | |
173 | } | |
174 | kernel = strdup(path); | |
175 | break; | |
176 | } | |
177 | } | |
178 | fclose(info); | |
179 | } | |
180 | ||
181 | static void | |
182 | kgdb_new_objfile(struct objfile *objfile) | |
183 | { | |
184 | #if 0 | |
185 | printf("XXX: %s(%p)\n", __func__, objfile); | |
186 | if (objfile != NULL) { | |
187 | goto out; | |
188 | } | |
189 | ||
190 | out: | |
191 | #endif | |
192 | if (kgdb_new_objfile_chain != NULL) | |
193 | kgdb_new_objfile_chain(objfile); | |
194 | } | |
195 | ||
196 | static CORE_ADDR | |
197 | kgdb_parse(const char *exp) | |
198 | { | |
199 | struct cleanup *old_chain; | |
200 | struct expression *expr; | |
201 | struct value *val; | |
202 | char *s; | |
203 | CORE_ADDR n; | |
204 | ||
205 | s = strdup(exp); | |
206 | old_chain = make_cleanup(free_current_contents, &expr); | |
207 | expr = parse_expression(s); | |
208 | val = (expr != NULL) ? evaluate_expression(expr) : NULL; | |
209 | n = (val != NULL) ? value_as_address(val) : 0; | |
210 | do_cleanups(old_chain); | |
211 | free(s); | |
212 | return (n); | |
213 | } | |
214 | ||
215 | #define MSGBUF_SEQ_TO_POS(size, seq) ((seq) % (size)) | |
216 | ||
3cdd3f79 MD |
217 | /* |
218 | * Fake-up because kernel may not have an ABI tag. | |
219 | */ | |
220 | static int | |
221 | kgdb_dummy_sniffer(bfd *bfd) | |
222 | { | |
223 | return(GDB_OSABI_DRAGONFLY_ELF); | |
224 | } | |
225 | ||
226 | ||
8b6a428f SS |
227 | static void |
228 | kgdb_init_target(void) | |
229 | { | |
230 | bfd *kern_bfd; | |
231 | int kern_desc; | |
232 | ||
3cdd3f79 MD |
233 | gdbarch_register_osabi_sniffer(bfd_arch_i386, bfd_target_elf_flavour, |
234 | kgdb_dummy_sniffer); | |
235 | ||
8b6a428f SS |
236 | kern_desc = open(kernel, O_RDONLY); |
237 | if (kern_desc == -1) | |
238 | errx(1, "couldn't open a kernel image"); | |
239 | ||
240 | kern_bfd = bfd_fdopenr(kernel, gnutarget, kern_desc); | |
241 | if (kern_bfd == NULL) { | |
242 | close(kern_desc); | |
243 | errx(1, "\"%s\": can't open to probe ABI: %s.", kernel, | |
244 | bfd_errmsg (bfd_get_error ())); | |
245 | } | |
246 | bfd_set_cacheable(kern_bfd, 1); | |
247 | ||
248 | if (!bfd_check_format (kern_bfd, bfd_object)) { | |
249 | bfd_close(kern_bfd); | |
250 | errx(1, "\"%s\": not in executable format: %s", kernel, | |
251 | bfd_errmsg(bfd_get_error())); | |
252 | } | |
253 | ||
254 | set_gdbarch_from_file (kern_bfd); | |
255 | bfd_close(kern_bfd); | |
256 | ||
257 | frame_unwind_prepend_unwinder(current_gdbarch, &kgdb_trgt_trapframe_unwind); | |
258 | ||
259 | symbol_file_add_main (kernel, 0); | |
260 | if (remote) | |
261 | push_remote_target (remote, 0); | |
262 | else | |
263 | kgdb_target(); | |
264 | } | |
265 | ||
266 | static void | |
267 | kgdb_display_msgbuf(void) | |
268 | { | |
c499d304 SS |
269 | uintptr_t addr; |
270 | struct msgbuf *bufp, buf; | |
271 | size_t rseq, wseq; | |
8b6a428f SS |
272 | char c; |
273 | ||
274 | /* | |
275 | * Display the unread portion of the message buffer. This gives the | |
276 | * user a some initial data to work from. | |
277 | */ | |
c499d304 SS |
278 | addr = lookup("_msgbufp"); |
279 | if (addr == 0) | |
280 | return; | |
281 | read_memory((CORE_ADDR)addr, (char *)&bufp, sizeof(bufp)); | |
282 | read_memory((CORE_ADDR)bufp, (char *)&buf, sizeof(buf)); | |
283 | if (buf.msg_size == 0 || buf.msg_bufr == buf.msg_bufx) | |
8b6a428f | 284 | return; |
c499d304 SS |
285 | rseq = MSGBUF_SEQ_TO_POS(buf.msg_size, buf.msg_bufr); |
286 | wseq = MSGBUF_SEQ_TO_POS(buf.msg_size, buf.msg_bufx); | |
8b6a428f SS |
287 | |
288 | printf("\nUnread portion of the kernel message buffer:\n"); | |
289 | while (rseq < wseq) { | |
c499d304 | 290 | read_memory((CORE_ADDR)buf.msg_ptr + rseq, &c, 1); |
8b6a428f SS |
291 | putchar(c); |
292 | rseq++; | |
c499d304 | 293 | if (rseq == buf.msg_size) |
8b6a428f SS |
294 | rseq = 0; |
295 | } | |
296 | if (c != '\n') | |
297 | putchar('\n'); | |
298 | putchar('\n'); | |
299 | } | |
300 | ||
301 | static void | |
302 | kgdb_init(char *argv0 __unused) | |
303 | { | |
304 | kgdb_init_target(); | |
305 | ||
306 | set_prompt("(kgdb) "); | |
307 | kgdb_display_msgbuf(); | |
0c3d4888 SS |
308 | print_stack_frame(get_selected_frame(NULL), |
309 | frame_relative_level(get_selected_frame(NULL)), 1); | |
8b6a428f SS |
310 | } |
311 | ||
312 | int | |
313 | main(int argc, char *argv[]) | |
314 | { | |
315 | char path[PATH_MAX]; | |
316 | struct stat st; | |
317 | struct captured_main_args args; | |
318 | char *s; | |
319 | int a, ch, quiet, writecore; | |
320 | ||
321 | dumpnr = -1; | |
322 | ||
323 | strlcpy(crashdir, "/var/crash", sizeof(crashdir)); | |
324 | s = getenv("KGDB_CRASH_DIR"); | |
325 | if (s != NULL) | |
326 | strlcpy(crashdir, s, sizeof(crashdir)); | |
327 | ||
328 | /* Convert long options into short options. */ | |
329 | for (a = 1; a < argc; a++) { | |
330 | s = argv[a]; | |
331 | if (s[0] == '-') { | |
332 | s++; | |
333 | /* Long options take either 1 or 2 dashes. */ | |
334 | if (s[0] == '-') | |
335 | s++; | |
336 | if (strcmp(s, "quiet") == 0) | |
337 | argv[a] = "-q"; | |
338 | else if (strcmp(s, "fullname") == 0) | |
339 | argv[a] = "-f"; | |
340 | } | |
341 | } | |
342 | ||
343 | quiet = 0; | |
344 | writecore = 0; | |
345 | ||
346 | while ((ch = getopt(argc, argv, "ac:d:fn:qr:vw")) != -1) { | |
347 | switch (ch) { | |
348 | case 'a': | |
349 | annotation_level++; | |
350 | break; | |
351 | case 'c': /* use given core file. */ | |
352 | if (vmcore != NULL) { | |
353 | warnx("option %c: can only be specified once", | |
354 | optopt); | |
355 | usage(); | |
356 | /* NOTREACHED */ | |
357 | } | |
358 | vmcore = strdup(optarg); | |
359 | break; | |
360 | case 'd': /* lookup dumps in given directory. */ | |
361 | strlcpy(crashdir, optarg, sizeof(crashdir)); | |
362 | break; | |
363 | case 'f': | |
364 | annotation_level = 1; | |
365 | break; | |
366 | case 'n': /* use dump with given number. */ | |
367 | dumpnr = strtol(optarg, &s, 0); | |
368 | if (dumpnr < 0 || *s != '\0') { | |
369 | warnx("option %c: invalid kernel dump number", | |
370 | optopt); | |
371 | usage(); | |
372 | /* NOTREACHED */ | |
373 | } | |
374 | break; | |
375 | case 'q': | |
376 | quiet = 1; | |
377 | break; | |
378 | case 'r': /* use given device for remote session. */ | |
379 | if (remote != NULL) { | |
380 | warnx("option %c: can only be specified once", | |
381 | optopt); | |
382 | usage(); | |
383 | /* NOTREACHED */ | |
384 | } | |
385 | remote = strdup(optarg); | |
386 | break; | |
387 | case 'v': /* increase verbosity. */ | |
388 | verbose++; | |
389 | break; | |
390 | case 'w': /* core file is writeable. */ | |
391 | writecore = 1; | |
392 | break; | |
393 | case '?': | |
394 | default: | |
395 | usage(); | |
396 | } | |
397 | } | |
398 | ||
399 | if (((vmcore != NULL) ? 1 : 0) + ((dumpnr >= 0) ? 1 : 0) + | |
400 | ((remote != NULL) ? 1 : 0) > 1) { | |
401 | warnx("options -c, -n and -r are mutually exclusive"); | |
402 | usage(); | |
403 | /* NOTREACHED */ | |
404 | } | |
405 | ||
406 | if (verbose > 1) | |
407 | warnx("using %s as the crash directory", crashdir); | |
408 | ||
409 | if (argc > optind) | |
410 | kernel = strdup(argv[optind++]); | |
411 | ||
412 | if (argc > optind && (dumpnr >= 0 || remote != NULL)) { | |
413 | warnx("options -n and -r do not take a core file. Ignored"); | |
414 | optind = argc; | |
415 | } | |
416 | ||
417 | if (dumpnr >= 0) { | |
418 | snprintf(path, sizeof(path), "%s/vmcore.%d", crashdir, dumpnr); | |
419 | if (stat(path, &st) == -1) | |
420 | err(1, path); | |
421 | if (!S_ISREG(st.st_mode)) | |
422 | errx(1, "%s: not a regular file", path); | |
423 | vmcore = strdup(path); | |
424 | } else if (remote != NULL && remote[0] != ':' && remote[0] != '|') { | |
425 | if (stat(remote, &st) != 0) { | |
426 | snprintf(path, sizeof(path), "/dev/%s", remote); | |
427 | if (stat(path, &st) != 0) { | |
428 | err(1, "%s", remote); | |
429 | /* NOTREACHED */ | |
430 | } | |
431 | free(remote); | |
432 | remote = strdup(path); | |
433 | } | |
434 | if (!S_ISCHR(st.st_mode) && !S_ISFIFO(st.st_mode)) { | |
435 | errx(1, "%s: not a special file, FIFO or socket", | |
436 | remote); | |
437 | /* NOTREACHED */ | |
438 | } | |
439 | } else if (argc > optind) { | |
440 | if (vmcore == NULL) | |
441 | vmcore = strdup(argv[optind++]); | |
442 | if (argc > optind) | |
443 | warnx("multiple core files specified. Ignored"); | |
444 | } else if (vmcore == NULL && kernel == NULL) { | |
445 | vmcore = strdup(_PATH_MEM); | |
446 | kernel = strdup(getbootfile()); | |
447 | } | |
448 | ||
449 | if (verbose) { | |
450 | if (vmcore != NULL) | |
451 | warnx("core file: %s", vmcore); | |
452 | if (remote != NULL) | |
453 | warnx("device file: %s", remote); | |
454 | if (kernel != NULL) | |
455 | warnx("kernel image: %s", kernel); | |
456 | } | |
457 | ||
458 | /* | |
459 | * At this point we must either have a core file or have a kernel | |
460 | * with a remote target. | |
461 | */ | |
462 | if (remote != NULL && kernel == NULL) { | |
463 | warnx("remote debugging requires a kernel"); | |
464 | usage(); | |
465 | /* NOTREACHED */ | |
466 | } | |
467 | if (vmcore == NULL && remote == NULL) { | |
468 | warnx("need a core file or a device for remote debugging"); | |
469 | usage(); | |
470 | /* NOTREACHED */ | |
471 | } | |
472 | ||
473 | /* If we don't have a kernel image yet, try to find one. */ | |
474 | if (kernel == NULL) { | |
475 | if (dumpnr >= 0) | |
476 | kernel_from_dumpnr(dumpnr); | |
477 | ||
478 | if (kernel == NULL) | |
479 | errx(1, "couldn't find a suitable kernel image"); | |
480 | if (verbose) | |
481 | warnx("kernel image: %s", kernel); | |
482 | } | |
483 | ||
484 | if (remote == NULL) { | |
485 | kvm = kvm_openfiles(kernel, vmcore, NULL, | |
486 | writecore ? O_RDWR : O_RDONLY, kvm_err); | |
487 | if (kvm == NULL) | |
488 | errx(1, kvm_err); | |
489 | atexit(kgdb_atexit); | |
490 | kgdb_thr_init(); | |
491 | } | |
492 | ||
493 | /* The libgdb code uses optind too. Reset it... */ | |
494 | optind = 0; | |
495 | ||
496 | memset (&args, 0, sizeof args); | |
497 | args.argv = argv; | |
498 | args.argc = 1 + quiet; | |
499 | if (quiet) | |
500 | argv[1] = "-q"; | |
501 | argv[args.argc] = NULL; | |
502 | args.use_windows = 0; | |
503 | args.interpreter_p = INTERP_CONSOLE; | |
504 | ||
505 | deprecated_init_ui_hook = kgdb_init; | |
506 | ||
507 | return (gdb_main(&args)); | |
508 | } |