]>
Commit | Line | Data |
---|---|---|
1ab3bf1b JG |
1 | /* Support for dumping and reloading various pieces of GDB's internal state. |
2 | Copyright 1992 Free Software Foundation, Inc. | |
3 | Contributed by Cygnus Support, using pieces from other GDB modules. | |
4 | ||
5 | This file is part of GDB. | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 2 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program; if not, write to the Free Software | |
19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
20 | ||
21 | /* This file provides support for dumping and then later reloading various | |
22 | portions of gdb's internal state. It was originally implemented to | |
23 | support a need for mapping in an image of gdb's symbol table from an | |
24 | external file, where this image was created by an external program, such | |
25 | as an incremental linker. However, it was generalized to enable future | |
26 | support for dumping and reloading various other useful pieces of gdb's | |
27 | internal state. | |
28 | ||
29 | State files have a fairly simple form which is intended to be easily | |
30 | extensible. The basic format is: | |
31 | ||
32 | <file-header> <state-data> <form-tree> | |
33 | ||
34 | Where: | |
35 | ||
36 | file-header A simple file-header containing a magic number | |
37 | so that gdb (and other readers) can quickly | |
38 | determine what kind of file this is, and a file | |
39 | offset to the root of the form-tree. | |
40 | ||
41 | state-data The "raw" state-data that is referenced by nodes | |
42 | in the form-tree. | |
43 | ||
44 | form-tree A tree of arbitrarily sized nodes containing | |
45 | information about gdb's internal state, and | |
46 | possibly referencing data in the state-data section | |
47 | of the file. Resembles DWARF in some respects. | |
48 | ||
49 | When writing a state file, a hole is left for the file-header at the | |
50 | beginning of the file, the state data is written immediately after the | |
51 | file header (while storing the file offsets and sizes back into the | |
52 | internal form-tree along the way), the form-tree itself is written | |
53 | at the end of the file, and then the file header is written by seeking | |
54 | back to the beginning of the file. This order is required because | |
55 | the form tree contains file offsets and sizes in the state data portion | |
56 | of the file, and the file header contains the file offset to the start | |
57 | of the form tree. | |
58 | ||
59 | Readers simply open the file, validate the magic number, seek to the | |
60 | root of the form-tree, and walk the tree looking for the information that | |
61 | they are interested in (and ignoring things that they aren't, or don't | |
62 | understand). | |
63 | ||
64 | */ | |
65 | ||
66 | ||
1ab3bf1b JG |
67 | #include "defs.h" |
68 | #include "symtab.h" | |
69 | #include "bfd.h" | |
70 | #include "symfile.h" | |
71 | #include "state.h" | |
72 | ||
73 | #ifndef SEEK_SET | |
74 | #define SEEK_SET 0 | |
75 | #endif | |
76 | ||
77 | #ifndef SEEK_END | |
78 | #define SEEK_END 2 | |
79 | #endif | |
80 | ||
81 | /* Inside the state file, the form-tree consists of a series of | |
82 | form-tree entries (FTE's). The parent/child/sibling relationships | |
83 | are implied by the ordering and by an explicit sibling reference | |
84 | in FTE's that have siblings. | |
85 | ||
86 | Specifically, given two sequential FTE's, say A and B, if B immediately | |
87 | follows A, and A does not have a sibling reference to B, then B is | |
88 | the first child of A. Otherwise B must be a sibling of A and A must | |
89 | have a sibling reference for it. | |
90 | ||
91 | Each FTE is simply an array of long integers, with at least three | |
92 | members. This form was chosen over a packed data form for simplicity | |
93 | in access, not having to worry about the relative sizes of the different | |
94 | integers (short, int, long), and not having to worry about alignment | |
95 | constraints. Also in the name of simplicity, every FTE has a sibling | |
96 | reference slot reserved for it, even if there are no siblings. | |
97 | ||
98 | The first value in an FTE is the size of the FTE in bytes, including | |
99 | the size value itself. The second entry contains a tag which indicates | |
100 | the type of the FTE. The third entry is a sibling reference, which either | |
101 | refers to a valid sibling node or is zero. Following is zero or more | |
102 | attributes, each of which consists of one or more long values. */ | |
103 | ||
104 | /* Tag names and codes. */ | |
105 | ||
106 | #define TAG_padding 0x0000 /* Padding */ | |
107 | #define TAG_objfile 0x0001 /* Dumped objfile */ | |
108 | ||
109 | /* Form names, codes, and macros. */ | |
110 | ||
111 | #define FORM_ABSREF 0x01 /* Next long is absolute file offset */ | |
112 | #define FORM_RELREF 0x02 /* Next long is relative file offset */ | |
113 | #define FORM_IVAL 0x03 /* Next long is int value */ | |
114 | #define FORM_ADDR 0x04 /* Next long is mem addr */ | |
115 | ||
116 | #define FORM_MASK 0xFF | |
117 | #define FORM_X(atr) ((atr) & FORM_MASK) | |
118 | ||
119 | /* Attribute names and codes. */ | |
120 | ||
121 | #define AT_sibling (0x0100 | FORM_RELREF) /* Reference to sibling node */ | |
122 | #define AT_name (0x0200 | FORM_ABSREF) /* Reference to a string */ | |
123 | #define AT_offset (0x0300 | FORM_ABSREF) /* Reference to generic data */ | |
124 | #define AT_size (0x0400 | FORM_IVAL) | |
125 | #define AT_addr (0x0500 | FORM_ADDR) | |
126 | #define AT_aux_addr (0x0600 | FORM_ADDR) | |
127 | ||
128 | /* */ | |
129 | ||
130 | static void | |
131 | load_symbols PARAMS ((FILE *)); | |
132 | ||
133 | static void | |
134 | dump_state_command PARAMS ((char *, int)); | |
135 | ||
136 | static void | |
137 | load_state_command PARAMS ((char *, int)); | |
138 | ||
139 | #ifdef HAVE_MMAP | |
140 | ||
141 | static void | |
142 | write_header PARAMS ((sfd *)); | |
143 | ||
144 | static void | |
145 | write_formtree PARAMS ((sfd *)); | |
146 | ||
147 | static void | |
148 | write_objfile_state PARAMS ((sfd *)); | |
149 | ||
150 | static void | |
151 | free_subtree PARAMS ((struct formnode *)); | |
152 | ||
153 | static void | |
154 | size_subtree PARAMS ((struct formnode *)); | |
155 | ||
156 | #endif | |
157 | ||
158 | struct formnode *formtree = NULL; | |
159 | ||
160 | /* ARGSUSED */ | |
161 | static void | |
162 | load_symbols (statefile) | |
163 | FILE *statefile; | |
164 | { | |
165 | ||
166 | #if 0 | |
167 | /* Discard old symbols. FIXME: This is essentially symbol_file_command's | |
168 | body when there is no name. Make it a common function that is | |
169 | called from each place. */ | |
170 | ||
171 | if (symfile_objfile) | |
172 | { | |
173 | free_objfile (symfile_objfile); | |
174 | } | |
175 | symfile_objfile = NULL; | |
176 | #endif | |
177 | ||
178 | #if 0 && defined (HAVE_MMAP) | |
179 | if (mtop > mbase) | |
180 | { | |
181 | warning ("internal error: mbase (%08x) != mtop (%08x)", | |
182 | mbase, mtop); | |
183 | munmap (mbase, mtop - mbase); | |
184 | } | |
185 | #endif /* HAVE_MMAP */ | |
186 | ||
187 | /* Getting new symbols may change our opinion about what is frameless. */ | |
188 | ||
189 | reinit_frame_cache (); | |
190 | ||
191 | } | |
192 | ||
193 | #ifdef HAVE_MMAP | |
194 | ||
195 | /* Allocate a form node */ | |
196 | ||
197 | static struct formnode * | |
198 | alloc_formnode () | |
199 | { | |
200 | struct formnode *fnp; | |
201 | ||
202 | fnp = (struct formnode *) xmalloc (sizeof (struct formnode)); | |
203 | (void) memset (fnp, 0, sizeof (struct formnode)); | |
204 | fnp -> sibling = formtree; | |
205 | formtree = fnp; | |
206 | return (fnp); | |
207 | } | |
208 | ||
209 | /* Recursively walk a form-tree from the specified node, freeing | |
210 | nodes from the bottom up. The concept is pretty simple, just free | |
211 | all the child nodes, then all the sibling nodes, then the node | |
212 | itself. */ | |
213 | ||
214 | static void | |
215 | free_subtree (fnp) | |
216 | struct formnode *fnp; | |
217 | { | |
218 | if (fnp != NULL) | |
219 | { | |
220 | free_subtree (fnp -> child); | |
221 | free_subtree (fnp -> sibling); | |
222 | if (fnp -> nodedata != NULL) | |
223 | { | |
224 | free (fnp -> nodedata); | |
225 | } | |
226 | free (fnp); | |
227 | } | |
228 | } | |
229 | ||
230 | /* Recursively walk a form-tree from the specified node, computing the | |
231 | size of each subtree from the bottom up. | |
232 | ||
233 | At each node, the file space that will be consumed by the subtree | |
234 | rooted in that node is the sum of all the subtrees rooted in each | |
235 | child node plus the size of the node itself. | |
236 | ||
237 | Thus for each node, we size the child subtrees, add to that our | |
238 | size, contribute this size towards the size of any parent node, and | |
239 | then ask any of our siblings to do the same. | |
240 | ||
241 | Also, once we know the size of any subtree rooted at this node, we | |
242 | can initialize the offset to the sibling node (if any). | |
243 | ||
244 | Since every form-tree node must have valid nodedata at this point, | |
245 | we detect and report a warning for any node that doesn't. */ | |
246 | ||
247 | static void | |
248 | size_subtree (fnp) | |
249 | struct formnode *fnp; | |
250 | { | |
251 | long *lp; | |
252 | ||
253 | if (fnp != NULL) | |
254 | { | |
255 | if (fnp -> nodedata == NULL) | |
256 | { | |
257 | warning ("internal error -- empty form node"); | |
258 | } | |
259 | else | |
260 | { | |
261 | size_subtree (fnp -> child); | |
262 | fnp -> treesize += *(long *) fnp -> nodedata; | |
263 | if (fnp -> parent != NULL) | |
264 | { | |
265 | fnp -> parent -> treesize += fnp -> treesize; | |
266 | } | |
267 | if (fnp -> sibling) | |
268 | { | |
269 | size_subtree (fnp -> sibling); | |
270 | lp = (long *) (fnp -> nodedata + 2 * sizeof (long)); | |
271 | *lp = fnp -> treesize; | |
272 | } | |
273 | } | |
274 | } | |
275 | } | |
276 | ||
277 | /* Recursively walk a form-tree from the specified node, writing | |
278 | nodes from the top down. */ | |
279 | ||
280 | static void | |
281 | write_subtree (fnp, asfd) | |
282 | struct formnode *fnp; | |
283 | sfd *asfd; | |
284 | { | |
285 | if (fnp != NULL) | |
286 | { | |
287 | if (fnp -> nodedata != NULL) | |
288 | { | |
289 | fwrite (fnp -> nodedata, *(long *) fnp -> nodedata, 1, asfd -> fp); | |
290 | } | |
291 | write_subtree (fnp -> child, asfd); | |
292 | write_subtree (fnp -> sibling, asfd); | |
293 | } | |
294 | } | |
295 | ||
296 | /* Free the entire current formtree. Called via do_cleanups, regardless | |
297 | of whether there is an error or not. */ | |
298 | ||
299 | static void | |
300 | free_formtree () | |
301 | { | |
302 | free_subtree (formtree); | |
303 | formtree = NULL; | |
304 | } | |
305 | ||
306 | /* Write out the file header. Generally this is done last, even though | |
307 | it is located at the start of the file, since we need to have file | |
308 | offset to where the annotated form tree was written, and it's size. */ | |
309 | ||
310 | static void | |
311 | write_header (asfd) | |
312 | sfd *asfd; | |
313 | { | |
314 | fseek (asfd -> fp, 0L, SEEK_SET); | |
315 | fwrite ((char *) &asfd -> hdr, sizeof (asfd -> hdr), 1, asfd -> fp); | |
316 | } | |
317 | ||
318 | /* Write out the annotated form tree. We should already have written out | |
319 | the state data, and noted the file offsets and sizes in each node of | |
320 | the form tree that references part of the state data. | |
321 | ||
322 | The form tree can be written anywhere in the file where there is room | |
323 | for it. Since there is always room at the end of the file, we write | |
324 | it there. We also need to record the file offset to the start of the | |
325 | form tree, and it's size, for future use when writing the file header. | |
326 | ||
327 | In order to compute the sibling references, we need to know, at | |
328 | each node, how much space will be consumed when all of that node's | |
329 | children nodes have been written. Thus we walk the tree, computing | |
330 | the sizes of the subtrees from the bottom up. At any node, the | |
331 | offset from the start of that node to the start of the sibling node | |
332 | is simply the size of the node plus the size of the subtree rooted | |
333 | in that node. */ | |
334 | ||
335 | static void | |
336 | write_formtree (asfd) | |
337 | sfd *asfd; | |
338 | { | |
339 | size_subtree (formtree); | |
340 | fseek (asfd -> fp, 0L, SEEK_END); | |
341 | asfd -> hdr.sf_ftoff = ftell (asfd -> fp); | |
342 | write_subtree (formtree, asfd); | |
343 | asfd -> hdr.sf_ftsize = ftell (asfd -> fp) - asfd -> hdr.sf_ftoff; | |
344 | } | |
345 | ||
346 | /* Note that we currently only support having one objfile with dumpable | |
347 | state. */ | |
348 | ||
349 | static void | |
350 | write_objfile_state (asfd) | |
351 | sfd *asfd; | |
352 | { | |
353 | struct objfile *objfile; | |
354 | struct formnode *fnp; | |
355 | PTR base; | |
356 | PTR breakval; | |
357 | long *lp; | |
358 | unsigned int ftesize; | |
359 | long ftebuf[64]; | |
360 | long foffset; | |
361 | ||
362 | /* First walk through the objfile list looking for the first objfile | |
363 | that is dumpable. */ | |
364 | ||
365 | for (objfile = object_files; objfile != NULL; objfile = objfile -> next) | |
366 | { | |
367 | if (objfile -> flags & OBJF_DUMPABLE) | |
368 | { | |
369 | break; | |
370 | } | |
371 | } | |
372 | ||
373 | if (objfile == NULL) | |
374 | { | |
375 | warning ("no dumpable objfile was found"); | |
376 | } | |
377 | else | |
378 | { | |
379 | fnp = alloc_formnode (); | |
380 | lp = ftebuf; | |
381 | ||
382 | lp++; /* Skip FTE size slot, filled in at the end. */ | |
383 | *lp++ = TAG_objfile; /* This is an objfile FTE */ | |
384 | *lp++ = 0; /* Zero the sibling reference slot. */ | |
385 | ||
386 | /* Build an AT_name attribute for the objfile's name, and write | |
387 | the name into the state data. */ | |
388 | ||
389 | *lp++ = AT_name; | |
390 | *lp++ = (long) ftell (asfd -> fp); | |
391 | fwrite (objfile -> name, strlen (objfile -> name) + 1, 1, asfd -> fp); | |
392 | ||
393 | /* Build an AT_addr attribute for the virtual address to which the | |
394 | objfile data is mapped (and needs to be remapped when read in). */ | |
395 | ||
396 | base = mmap_base (); | |
397 | *lp++ = AT_addr; | |
398 | *lp++ = (long) base; | |
399 | ||
400 | /* Build an AT_aux_addr attribute for the address of the objfile | |
401 | structure itself, within the dumpable data. When we read the objfile | |
402 | back in, we use this address as the pointer the "struct objfile". */ | |
403 | ||
404 | *lp++ = AT_aux_addr; | |
405 | *lp++ = (long) objfile; | |
406 | ||
407 | /* Reposition in state file to next paging boundry so we can mmap the | |
408 | dumpable objfile data when we reload it. */ | |
409 | ||
410 | foffset = (long) mmap_page_align ((PTR) ftell (asfd -> fp)); | |
411 | fseek (asfd -> fp, foffset, SEEK_SET); | |
412 | ||
413 | /* Build an AT_offset attribute for the offset in the state file to | |
414 | the start of the dumped objfile data. */ | |
415 | ||
416 | *lp++ = AT_offset; | |
417 | *lp++ = (long) ftell (asfd -> fp); | |
418 | ||
419 | /* Build an AT_size attribute for the size of the dumped objfile data. */ | |
420 | ||
421 | breakval = mmap_sbrk (0); | |
422 | *lp++ = AT_size; | |
423 | *lp++ = breakval - base; | |
424 | ||
425 | /* Write the dumpable data. */ | |
426 | ||
427 | fwrite ((char *) base, breakval - base, 1, asfd -> fp); | |
428 | ||
429 | /* Now finish up the FTE by filling in the size slot based on | |
430 | how much of the ftebuf we have used, allocate some memory for | |
431 | it hung off the form tree node, and copy it there. */ | |
432 | ||
433 | ftebuf[0] = (lp - ftebuf) * sizeof (ftebuf[0]); | |
434 | fnp -> nodedata = (char *) xmalloc (ftebuf[0]); | |
435 | memcpy (fnp -> nodedata, ftebuf, ftebuf[0]); | |
436 | } | |
437 | } | |
438 | ||
439 | static void | |
440 | load_state_command (arg_string, from_tty) | |
441 | char *arg_string; | |
442 | int from_tty; | |
443 | { | |
444 | char *filename; | |
445 | char **argv; | |
446 | FILE *fp; | |
447 | struct cleanup *cleanups; | |
448 | ||
449 | dont_repeat (); | |
450 | ||
451 | if (arg_string == NULL) | |
452 | { | |
453 | error ("load-state takes a file name and optional state specifiers"); | |
454 | } | |
455 | else if ((argv = buildargv (arg_string)) == NULL) | |
456 | { | |
457 | fatal ("virtual memory exhausted.", 0); | |
458 | } | |
459 | cleanups = make_cleanup (freeargv, argv); | |
460 | ||
461 | filename = tilde_expand (*argv); | |
462 | make_cleanup (free, filename); | |
463 | ||
464 | if ((fp = fopen (filename, "r")) == NULL) | |
465 | { | |
466 | perror_with_name (filename); | |
467 | } | |
468 | make_cleanup (fclose, fp); | |
469 | immediate_quit++; | |
470 | ||
471 | while (*++argv != NULL) | |
472 | { | |
473 | if (strcmp (*argv, "symbols") == 0) | |
474 | { | |
475 | if (from_tty | |
476 | && !query ("load symbol table state from file \"%s\"? ", | |
477 | filename)) | |
478 | { | |
479 | error ("Not confirmed."); | |
480 | } | |
481 | load_symbols (fp); | |
482 | } | |
483 | else | |
484 | { | |
485 | error ("unknown state specifier '%s'", *argv); | |
486 | } | |
487 | } | |
488 | immediate_quit--; | |
489 | do_cleanups (cleanups); | |
490 | } | |
491 | ||
492 | /* ARGSUSED */ | |
493 | static void | |
494 | dump_state_command (arg_string, from_tty) | |
495 | char *arg_string; | |
496 | int from_tty; | |
497 | { | |
498 | char *filename; | |
499 | char **argv; | |
500 | sfd *asfd; | |
501 | struct cleanup *cleanups; | |
502 | ||
503 | dont_repeat (); | |
504 | ||
505 | if (arg_string == NULL) | |
506 | { | |
507 | error ("dump-state takes a file name and state specifiers"); | |
508 | } | |
509 | else if ((argv = buildargv (arg_string)) == NULL) | |
510 | { | |
511 | fatal ("virtual memory exhausted.", 0); | |
512 | } | |
513 | cleanups = make_cleanup (freeargv, argv); | |
514 | ||
515 | filename = tilde_expand (*argv); | |
516 | make_cleanup (free, filename); | |
517 | ||
518 | /* Now attempt to create a fresh state file. */ | |
519 | ||
520 | if ((asfd = sfd_fopen (filename, "w")) == NULL) | |
521 | { | |
522 | perror_with_name (filename); | |
523 | } | |
524 | make_cleanup (sfd_fclose, asfd); | |
525 | make_cleanup (free_formtree, NULL); | |
526 | immediate_quit++; | |
527 | ||
528 | /* Now that we have an open and initialized state file, seek to the | |
529 | proper offset to start writing state data and the process the | |
530 | arguments. For each argument, write the state data and initialize | |
531 | a form-tree node for each piece of state data. */ | |
532 | ||
533 | fseek (asfd -> fp, sizeof (sf_hdr), SEEK_SET); | |
534 | while (*++argv != NULL) | |
535 | { | |
536 | if (strcmp (*argv, "objfile") == 0) | |
537 | { | |
538 | write_objfile_state (asfd); | |
539 | } | |
540 | else | |
541 | { | |
542 | error ("unknown state specifier '%s'", *argv); | |
543 | } | |
544 | ||
545 | } | |
546 | ||
547 | /* We have written any state data. All that is left to do now is | |
548 | write the form-tree and the file header. */ | |
549 | ||
550 | write_formtree (asfd); | |
551 | write_header (asfd); | |
552 | ||
553 | immediate_quit--; | |
554 | do_cleanups (cleanups); | |
555 | } | |
556 | ||
557 | static char * | |
558 | find_fte_by_walk (thisfte, endfte, tag) | |
559 | char *thisfte; | |
560 | char *endfte; | |
561 | long tag; | |
562 | { | |
563 | char *found = NULL; | |
564 | char *nextfte; | |
565 | long thistag; | |
566 | long thissize; | |
567 | long siboffset; | |
568 | ||
569 | while (thisfte < endfte) | |
570 | { | |
571 | if ((thistag = *(long *)(thisfte + sizeof (long))) == tag) | |
572 | { | |
573 | found = thisfte; | |
574 | break; | |
575 | } | |
576 | else | |
577 | { | |
578 | thissize = *(long *)(thisfte); | |
579 | siboffset = *(long *)(thisfte + (2 * sizeof (long))); | |
580 | nextfte = thisfte + (siboffset != 0 ? siboffset : thissize); | |
581 | found = find_fte_by_walk (thisfte + thissize, nextfte, tag); | |
582 | thisfte = nextfte; | |
583 | } | |
584 | } | |
585 | return (found); | |
586 | } | |
587 | ||
588 | /* Walk the form-tree looking for a specific FTE type. Returns the first | |
589 | one found that matches the specified tag. */ | |
590 | ||
591 | static char * | |
592 | find_fte (asfd, tag) | |
593 | sfd *asfd; | |
594 | long tag; | |
595 | { | |
596 | char *ftbase; | |
597 | char *ftend; | |
598 | char *ftep; | |
599 | char *found = NULL; | |
600 | ||
601 | if (fseek (asfd -> fp, asfd -> hdr.sf_ftoff, SEEK_SET) == 0) | |
602 | { | |
603 | ftbase = xmalloc (asfd -> hdr.sf_ftsize); | |
604 | ftend = ftbase + asfd -> hdr.sf_ftsize; | |
605 | if (fread (ftbase, asfd -> hdr.sf_ftsize, 1, asfd -> fp) == 1) | |
606 | { | |
607 | ftep = find_fte_by_walk (ftbase, ftend, tag); | |
608 | if (ftep != NULL) | |
609 | { | |
610 | found = xmalloc (*(long *)ftep); | |
611 | memcpy (found, ftep, (int) *(long *)ftep); | |
612 | } | |
613 | } | |
614 | free (ftbase); | |
615 | } | |
616 | return (found); | |
617 | } | |
618 | ||
619 | struct objfile * | |
620 | objfile_from_statefile (asfd) | |
621 | sfd *asfd; | |
622 | { | |
623 | struct objfile *objfile = NULL; | |
624 | char *ftep; | |
625 | long *thisattr; | |
626 | long *endattr; | |
627 | PTR base; | |
628 | long foffset; | |
629 | long mapsize; | |
630 | ||
631 | ftep = find_fte (asfd, TAG_objfile); | |
632 | thisattr = (long *) (ftep + 3 * sizeof (long)); | |
633 | endattr = (long *) (ftep + *(long *)ftep); | |
634 | while (thisattr < endattr) | |
635 | { | |
636 | switch (*thisattr++) | |
637 | { | |
638 | case AT_name: | |
639 | /* Ignore for now */ | |
640 | thisattr++; | |
641 | break; | |
642 | case AT_addr: | |
643 | base = (PTR) *thisattr++; | |
644 | break; | |
645 | case AT_aux_addr: | |
646 | objfile = (struct objfile *) *thisattr++; | |
647 | break; | |
648 | case AT_offset: | |
649 | foffset = *thisattr++; | |
650 | break; | |
651 | case AT_size: | |
652 | mapsize = *thisattr++; | |
653 | break; | |
654 | } | |
655 | } | |
656 | if (mmap_remap (base, mapsize, (int) fileno (asfd -> fp), foffset) != base) | |
657 | { | |
658 | print_sys_errmsg (asfd -> filename, errno); | |
659 | error ("mapping failed"); | |
660 | } | |
661 | ||
662 | return (objfile); | |
663 | } | |
664 | ||
665 | #else | |
666 | ||
667 | struct objfile * | |
668 | objfile_from_statefile (asfd) | |
669 | sfd *asfd; | |
670 | { | |
671 | error ("this version of gdb doesn't support reloading symtabs from state files"); | |
672 | } | |
673 | ||
674 | #endif /* HAVE_MMAP */ | |
675 | ||
676 | /* Close a state file, freeing all memory that was used by the state | |
677 | file descriptor, closing the raw file pointer, etc. */ | |
678 | ||
679 | void | |
680 | sfd_fclose (asfd) | |
681 | sfd *asfd; | |
682 | { | |
683 | if (asfd != NULL) | |
684 | { | |
685 | if (asfd -> fp != NULL) | |
686 | { | |
687 | fclose (asfd -> fp); | |
688 | } | |
689 | if (asfd -> filename != NULL) | |
690 | { | |
691 | free (asfd -> filename); | |
692 | } | |
693 | free (asfd); | |
694 | } | |
695 | } | |
696 | ||
697 | /* Given the name of a possible statefile, and flags to use to open it, | |
698 | try to open the file and prepare it for use. | |
699 | ||
700 | If the flags contain 'r', then we want to read an existing state | |
701 | file, so attempt to read in the state file header and determine if this | |
702 | is a valid state file. If not, return NULL. | |
703 | ||
704 | Returns a pointer to a properly initialized state file descriptor if | |
705 | successful. */ | |
706 | ||
707 | sfd * | |
708 | sfd_fopen (name, flags) | |
709 | char *name; | |
710 | char *flags; | |
711 | { | |
712 | int success = 0; | |
713 | sfd *asfd; | |
714 | ||
715 | asfd = (sfd *) xmalloc (sizeof (sfd)); | |
716 | (void) memset (asfd, 0, sizeof (sfd)); | |
717 | asfd -> filename = xmalloc (strlen (name) + 1); | |
718 | (void) strcpy (asfd -> filename, name); | |
719 | ||
720 | if ((asfd -> fp = fopen (asfd -> filename, flags)) != NULL) | |
721 | { | |
722 | /* We have the file, now see if we are reading an existing file | |
723 | or writing to a new file. We don't currently support "rw". */ | |
724 | if (strchr (flags, 'r') != NULL) | |
725 | { | |
726 | if (fread ((char *) &asfd -> hdr, sizeof (asfd -> hdr), 1, | |
727 | asfd -> fp) == 1) | |
728 | { | |
729 | if (SF_GOOD_MAGIC (asfd)) | |
730 | { | |
731 | success = 1; | |
732 | } | |
733 | } | |
734 | } | |
735 | else | |
736 | { | |
737 | /* This is a new state file. Initialize various things. */ | |
738 | asfd -> hdr.sf_mag0 = SF_MAG0; | |
739 | asfd -> hdr.sf_mag1 = SF_MAG1; | |
740 | asfd -> hdr.sf_mag2 = SF_MAG2; | |
741 | asfd -> hdr.sf_mag3 = SF_MAG3; | |
742 | success = 1; | |
743 | } | |
744 | } | |
745 | ||
746 | if (!success) | |
747 | { | |
748 | sfd_fclose (asfd); | |
749 | asfd = NULL; | |
750 | } | |
751 | return (asfd); | |
752 | ||
753 | } | |
754 | ||
755 | \f | |
756 | void | |
757 | _initialize_state () | |
758 | { | |
759 | ||
760 | #ifdef HAVE_MMAP | |
761 | ||
762 | add_com ("load-state", class_support, load_state_command, | |
763 | "Load some saved gdb state from FILE.\n\ | |
764 | Select and load some portion of gdb's saved state from the specified file.\n\ | |
765 | The dump-state command may be used to save various portions of gdb's\n\ | |
766 | internal state."); | |
767 | ||
768 | add_com ("dump-state", class_support, dump_state_command, | |
769 | "Dump some of gdb's state to FILE.\n\ | |
770 | Select and dump some portion of gdb's internal state to the specified file.\n\ | |
771 | The load-state command may be used to reload various portions of gdb's\n\ | |
772 | internal state from the file."); | |
773 | ||
774 | #endif /* HAVE_MMAP */ | |
775 | ||
776 | } |