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; | |
1ab3bf1b JG |
201 | fnp = (struct formnode *) xmalloc (sizeof (struct formnode)); |
202 | (void) memset (fnp, 0, sizeof (struct formnode)); | |
203 | fnp -> sibling = formtree; | |
204 | formtree = fnp; | |
205 | return (fnp); | |
206 | } | |
207 | ||
208 | /* Recursively walk a form-tree from the specified node, freeing | |
209 | nodes from the bottom up. The concept is pretty simple, just free | |
210 | all the child nodes, then all the sibling nodes, then the node | |
211 | itself. */ | |
212 | ||
213 | static void | |
214 | free_subtree (fnp) | |
215 | struct formnode *fnp; | |
216 | { | |
217 | if (fnp != NULL) | |
218 | { | |
219 | free_subtree (fnp -> child); | |
220 | free_subtree (fnp -> sibling); | |
221 | if (fnp -> nodedata != NULL) | |
222 | { | |
223 | free (fnp -> nodedata); | |
224 | } | |
225 | free (fnp); | |
226 | } | |
227 | } | |
228 | ||
229 | /* Recursively walk a form-tree from the specified node, computing the | |
230 | size of each subtree from the bottom up. | |
231 | ||
232 | At each node, the file space that will be consumed by the subtree | |
233 | rooted in that node is the sum of all the subtrees rooted in each | |
234 | child node plus the size of the node itself. | |
235 | ||
236 | Thus for each node, we size the child subtrees, add to that our | |
237 | size, contribute this size towards the size of any parent node, and | |
238 | then ask any of our siblings to do the same. | |
239 | ||
240 | Also, once we know the size of any subtree rooted at this node, we | |
241 | can initialize the offset to the sibling node (if any). | |
242 | ||
243 | Since every form-tree node must have valid nodedata at this point, | |
244 | we detect and report a warning for any node that doesn't. */ | |
245 | ||
246 | static void | |
247 | size_subtree (fnp) | |
248 | struct formnode *fnp; | |
249 | { | |
250 | long *lp; | |
251 | ||
252 | if (fnp != NULL) | |
253 | { | |
254 | if (fnp -> nodedata == NULL) | |
255 | { | |
256 | warning ("internal error -- empty form node"); | |
257 | } | |
258 | else | |
259 | { | |
260 | size_subtree (fnp -> child); | |
261 | fnp -> treesize += *(long *) fnp -> nodedata; | |
262 | if (fnp -> parent != NULL) | |
263 | { | |
264 | fnp -> parent -> treesize += fnp -> treesize; | |
265 | } | |
266 | if (fnp -> sibling) | |
267 | { | |
268 | size_subtree (fnp -> sibling); | |
269 | lp = (long *) (fnp -> nodedata + 2 * sizeof (long)); | |
270 | *lp = fnp -> treesize; | |
271 | } | |
272 | } | |
273 | } | |
274 | } | |
275 | ||
276 | /* Recursively walk a form-tree from the specified node, writing | |
277 | nodes from the top down. */ | |
278 | ||
279 | static void | |
280 | write_subtree (fnp, asfd) | |
281 | struct formnode *fnp; | |
282 | sfd *asfd; | |
283 | { | |
284 | if (fnp != NULL) | |
285 | { | |
286 | if (fnp -> nodedata != NULL) | |
287 | { | |
288 | fwrite (fnp -> nodedata, *(long *) fnp -> nodedata, 1, asfd -> fp); | |
289 | } | |
290 | write_subtree (fnp -> child, asfd); | |
291 | write_subtree (fnp -> sibling, asfd); | |
292 | } | |
293 | } | |
294 | ||
295 | /* Free the entire current formtree. Called via do_cleanups, regardless | |
296 | of whether there is an error or not. */ | |
297 | ||
298 | static void | |
299 | free_formtree () | |
300 | { | |
301 | free_subtree (formtree); | |
302 | formtree = NULL; | |
303 | } | |
304 | ||
305 | /* Write out the file header. Generally this is done last, even though | |
306 | it is located at the start of the file, since we need to have file | |
307 | offset to where the annotated form tree was written, and it's size. */ | |
308 | ||
309 | static void | |
310 | write_header (asfd) | |
311 | sfd *asfd; | |
312 | { | |
313 | fseek (asfd -> fp, 0L, SEEK_SET); | |
314 | fwrite ((char *) &asfd -> hdr, sizeof (asfd -> hdr), 1, asfd -> fp); | |
315 | } | |
316 | ||
317 | /* Write out the annotated form tree. We should already have written out | |
318 | the state data, and noted the file offsets and sizes in each node of | |
319 | the form tree that references part of the state data. | |
320 | ||
321 | The form tree can be written anywhere in the file where there is room | |
322 | for it. Since there is always room at the end of the file, we write | |
323 | it there. We also need to record the file offset to the start of the | |
324 | form tree, and it's size, for future use when writing the file header. | |
325 | ||
326 | In order to compute the sibling references, we need to know, at | |
327 | each node, how much space will be consumed when all of that node's | |
328 | children nodes have been written. Thus we walk the tree, computing | |
329 | the sizes of the subtrees from the bottom up. At any node, the | |
330 | offset from the start of that node to the start of the sibling node | |
331 | is simply the size of the node plus the size of the subtree rooted | |
332 | in that node. */ | |
333 | ||
334 | static void | |
335 | write_formtree (asfd) | |
336 | sfd *asfd; | |
337 | { | |
338 | size_subtree (formtree); | |
339 | fseek (asfd -> fp, 0L, SEEK_END); | |
340 | asfd -> hdr.sf_ftoff = ftell (asfd -> fp); | |
341 | write_subtree (formtree, asfd); | |
342 | asfd -> hdr.sf_ftsize = ftell (asfd -> fp) - asfd -> hdr.sf_ftoff; | |
343 | } | |
344 | ||
345 | /* Note that we currently only support having one objfile with dumpable | |
346 | state. */ | |
347 | ||
348 | static void | |
349 | write_objfile_state (asfd) | |
350 | sfd *asfd; | |
351 | { | |
352 | struct objfile *objfile; | |
353 | struct formnode *fnp; | |
354 | PTR base; | |
355 | PTR breakval; | |
356 | long *lp; | |
357 | unsigned int ftesize; | |
358 | long ftebuf[64]; | |
359 | long foffset; | |
360 | ||
361 | /* First walk through the objfile list looking for the first objfile | |
362 | that is dumpable. */ | |
363 | ||
364 | for (objfile = object_files; objfile != NULL; objfile = objfile -> next) | |
365 | { | |
366 | if (objfile -> flags & OBJF_DUMPABLE) | |
367 | { | |
368 | break; | |
369 | } | |
370 | } | |
371 | ||
372 | if (objfile == NULL) | |
373 | { | |
374 | warning ("no dumpable objfile was found"); | |
375 | } | |
376 | else | |
377 | { | |
378 | fnp = alloc_formnode (); | |
379 | lp = ftebuf; | |
380 | ||
381 | lp++; /* Skip FTE size slot, filled in at the end. */ | |
382 | *lp++ = TAG_objfile; /* This is an objfile FTE */ | |
383 | *lp++ = 0; /* Zero the sibling reference slot. */ | |
384 | ||
385 | /* Build an AT_name attribute for the objfile's name, and write | |
386 | the name into the state data. */ | |
387 | ||
388 | *lp++ = AT_name; | |
389 | *lp++ = (long) ftell (asfd -> fp); | |
390 | fwrite (objfile -> name, strlen (objfile -> name) + 1, 1, asfd -> fp); | |
391 | ||
392 | /* Build an AT_addr attribute for the virtual address to which the | |
393 | objfile data is mapped (and needs to be remapped when read in). */ | |
394 | ||
395 | base = mmap_base (); | |
396 | *lp++ = AT_addr; | |
397 | *lp++ = (long) base; | |
398 | ||
399 | /* Build an AT_aux_addr attribute for the address of the objfile | |
400 | structure itself, within the dumpable data. When we read the objfile | |
401 | back in, we use this address as the pointer the "struct objfile". */ | |
402 | ||
403 | *lp++ = AT_aux_addr; | |
404 | *lp++ = (long) objfile; | |
405 | ||
406 | /* Reposition in state file to next paging boundry so we can mmap the | |
407 | dumpable objfile data when we reload it. */ | |
408 | ||
409 | foffset = (long) mmap_page_align ((PTR) ftell (asfd -> fp)); | |
410 | fseek (asfd -> fp, foffset, SEEK_SET); | |
411 | ||
412 | /* Build an AT_offset attribute for the offset in the state file to | |
413 | the start of the dumped objfile data. */ | |
414 | ||
415 | *lp++ = AT_offset; | |
416 | *lp++ = (long) ftell (asfd -> fp); | |
417 | ||
418 | /* Build an AT_size attribute for the size of the dumped objfile data. */ | |
419 | ||
420 | breakval = mmap_sbrk (0); | |
421 | *lp++ = AT_size; | |
422 | *lp++ = breakval - base; | |
423 | ||
424 | /* Write the dumpable data. */ | |
425 | ||
426 | fwrite ((char *) base, breakval - base, 1, asfd -> fp); | |
427 | ||
428 | /* Now finish up the FTE by filling in the size slot based on | |
429 | how much of the ftebuf we have used, allocate some memory for | |
430 | it hung off the form tree node, and copy it there. */ | |
431 | ||
432 | ftebuf[0] = (lp - ftebuf) * sizeof (ftebuf[0]); | |
433 | fnp -> nodedata = (char *) xmalloc (ftebuf[0]); | |
434 | memcpy (fnp -> nodedata, ftebuf, ftebuf[0]); | |
435 | } | |
436 | } | |
437 | ||
438 | static void | |
439 | load_state_command (arg_string, from_tty) | |
440 | char *arg_string; | |
441 | int from_tty; | |
442 | { | |
443 | char *filename; | |
444 | char **argv; | |
445 | FILE *fp; | |
446 | struct cleanup *cleanups; | |
447 | ||
448 | dont_repeat (); | |
449 | ||
450 | if (arg_string == NULL) | |
451 | { | |
452 | error ("load-state takes a file name and optional state specifiers"); | |
453 | } | |
454 | else if ((argv = buildargv (arg_string)) == NULL) | |
455 | { | |
456 | fatal ("virtual memory exhausted.", 0); | |
457 | } | |
458 | cleanups = make_cleanup (freeargv, argv); | |
459 | ||
460 | filename = tilde_expand (*argv); | |
461 | make_cleanup (free, filename); | |
462 | ||
a944e79a | 463 | if ((fp = fopen (filename, FOPEN_RB)) == NULL) |
1ab3bf1b JG |
464 | { |
465 | perror_with_name (filename); | |
466 | } | |
467 | make_cleanup (fclose, fp); | |
468 | immediate_quit++; | |
469 | ||
470 | while (*++argv != NULL) | |
471 | { | |
2e4964ad | 472 | if (STREQ (*argv, "symbols")) |
1ab3bf1b JG |
473 | { |
474 | if (from_tty | |
475 | && !query ("load symbol table state from file \"%s\"? ", | |
476 | filename)) | |
477 | { | |
478 | error ("Not confirmed."); | |
479 | } | |
480 | load_symbols (fp); | |
481 | } | |
482 | else | |
483 | { | |
484 | error ("unknown state specifier '%s'", *argv); | |
485 | } | |
486 | } | |
487 | immediate_quit--; | |
488 | do_cleanups (cleanups); | |
489 | } | |
490 | ||
491 | /* ARGSUSED */ | |
492 | static void | |
493 | dump_state_command (arg_string, from_tty) | |
494 | char *arg_string; | |
495 | int from_tty; | |
496 | { | |
497 | char *filename; | |
498 | char **argv; | |
499 | sfd *asfd; | |
500 | struct cleanup *cleanups; | |
501 | ||
502 | dont_repeat (); | |
503 | ||
504 | if (arg_string == NULL) | |
505 | { | |
506 | error ("dump-state takes a file name and state specifiers"); | |
507 | } | |
508 | else if ((argv = buildargv (arg_string)) == NULL) | |
509 | { | |
510 | fatal ("virtual memory exhausted.", 0); | |
511 | } | |
512 | cleanups = make_cleanup (freeargv, argv); | |
513 | ||
514 | filename = tilde_expand (*argv); | |
515 | make_cleanup (free, filename); | |
516 | ||
517 | /* Now attempt to create a fresh state file. */ | |
518 | ||
a944e79a | 519 | if ((asfd = sfd_fopen (filename, FOPEN_WB)) == NULL) |
1ab3bf1b JG |
520 | { |
521 | perror_with_name (filename); | |
522 | } | |
523 | make_cleanup (sfd_fclose, asfd); | |
524 | make_cleanup (free_formtree, NULL); | |
525 | immediate_quit++; | |
526 | ||
527 | /* Now that we have an open and initialized state file, seek to the | |
528 | proper offset to start writing state data and the process the | |
529 | arguments. For each argument, write the state data and initialize | |
530 | a form-tree node for each piece of state data. */ | |
531 | ||
532 | fseek (asfd -> fp, sizeof (sf_hdr), SEEK_SET); | |
533 | while (*++argv != NULL) | |
534 | { | |
2e4964ad | 535 | if (STREQ (*argv, "objfile")) |
1ab3bf1b JG |
536 | { |
537 | write_objfile_state (asfd); | |
538 | } | |
539 | else | |
540 | { | |
541 | error ("unknown state specifier '%s'", *argv); | |
542 | } | |
543 | ||
544 | } | |
545 | ||
546 | /* We have written any state data. All that is left to do now is | |
547 | write the form-tree and the file header. */ | |
548 | ||
549 | write_formtree (asfd); | |
550 | write_header (asfd); | |
551 | ||
552 | immediate_quit--; | |
553 | do_cleanups (cleanups); | |
554 | } | |
555 | ||
556 | static char * | |
557 | find_fte_by_walk (thisfte, endfte, tag) | |
558 | char *thisfte; | |
559 | char *endfte; | |
560 | long tag; | |
561 | { | |
562 | char *found = NULL; | |
563 | char *nextfte; | |
564 | long thistag; | |
565 | long thissize; | |
566 | long siboffset; | |
567 | ||
568 | while (thisfte < endfte) | |
569 | { | |
570 | if ((thistag = *(long *)(thisfte + sizeof (long))) == tag) | |
571 | { | |
572 | found = thisfte; | |
573 | break; | |
574 | } | |
575 | else | |
576 | { | |
577 | thissize = *(long *)(thisfte); | |
578 | siboffset = *(long *)(thisfte + (2 * sizeof (long))); | |
579 | nextfte = thisfte + (siboffset != 0 ? siboffset : thissize); | |
580 | found = find_fte_by_walk (thisfte + thissize, nextfte, tag); | |
581 | thisfte = nextfte; | |
582 | } | |
583 | } | |
584 | return (found); | |
585 | } | |
586 | ||
587 | /* Walk the form-tree looking for a specific FTE type. Returns the first | |
588 | one found that matches the specified tag. */ | |
589 | ||
590 | static char * | |
591 | find_fte (asfd, tag) | |
592 | sfd *asfd; | |
593 | long tag; | |
594 | { | |
595 | char *ftbase; | |
596 | char *ftend; | |
597 | char *ftep; | |
598 | char *found = NULL; | |
599 | ||
600 | if (fseek (asfd -> fp, asfd -> hdr.sf_ftoff, SEEK_SET) == 0) | |
601 | { | |
602 | ftbase = xmalloc (asfd -> hdr.sf_ftsize); | |
603 | ftend = ftbase + asfd -> hdr.sf_ftsize; | |
604 | if (fread (ftbase, asfd -> hdr.sf_ftsize, 1, asfd -> fp) == 1) | |
605 | { | |
606 | ftep = find_fte_by_walk (ftbase, ftend, tag); | |
607 | if (ftep != NULL) | |
608 | { | |
609 | found = xmalloc (*(long *)ftep); | |
610 | memcpy (found, ftep, (int) *(long *)ftep); | |
611 | } | |
612 | } | |
613 | free (ftbase); | |
614 | } | |
615 | return (found); | |
616 | } | |
617 | ||
618 | struct objfile * | |
619 | objfile_from_statefile (asfd) | |
620 | sfd *asfd; | |
621 | { | |
622 | struct objfile *objfile = NULL; | |
623 | char *ftep; | |
624 | long *thisattr; | |
625 | long *endattr; | |
626 | PTR base; | |
627 | long foffset; | |
628 | long mapsize; | |
629 | ||
630 | ftep = find_fte (asfd, TAG_objfile); | |
631 | thisattr = (long *) (ftep + 3 * sizeof (long)); | |
632 | endattr = (long *) (ftep + *(long *)ftep); | |
633 | while (thisattr < endattr) | |
634 | { | |
635 | switch (*thisattr++) | |
636 | { | |
637 | case AT_name: | |
638 | /* Ignore for now */ | |
639 | thisattr++; | |
640 | break; | |
641 | case AT_addr: | |
642 | base = (PTR) *thisattr++; | |
643 | break; | |
644 | case AT_aux_addr: | |
645 | objfile = (struct objfile *) *thisattr++; | |
646 | break; | |
647 | case AT_offset: | |
648 | foffset = *thisattr++; | |
649 | break; | |
650 | case AT_size: | |
651 | mapsize = *thisattr++; | |
652 | break; | |
653 | } | |
654 | } | |
655 | if (mmap_remap (base, mapsize, (int) fileno (asfd -> fp), foffset) != base) | |
656 | { | |
657 | print_sys_errmsg (asfd -> filename, errno); | |
658 | error ("mapping failed"); | |
659 | } | |
660 | ||
661 | return (objfile); | |
662 | } | |
663 | ||
664 | #else | |
665 | ||
666 | struct objfile * | |
667 | objfile_from_statefile (asfd) | |
668 | sfd *asfd; | |
669 | { | |
670 | error ("this version of gdb doesn't support reloading symtabs from state files"); | |
671 | } | |
672 | ||
673 | #endif /* HAVE_MMAP */ | |
674 | ||
675 | /* Close a state file, freeing all memory that was used by the state | |
676 | file descriptor, closing the raw file pointer, etc. */ | |
677 | ||
678 | void | |
679 | sfd_fclose (asfd) | |
680 | sfd *asfd; | |
681 | { | |
682 | if (asfd != NULL) | |
683 | { | |
684 | if (asfd -> fp != NULL) | |
685 | { | |
686 | fclose (asfd -> fp); | |
687 | } | |
688 | if (asfd -> filename != NULL) | |
689 | { | |
690 | free (asfd -> filename); | |
691 | } | |
692 | free (asfd); | |
693 | } | |
694 | } | |
695 | ||
696 | /* Given the name of a possible statefile, and flags to use to open it, | |
697 | try to open the file and prepare it for use. | |
698 | ||
699 | If the flags contain 'r', then we want to read an existing state | |
700 | file, so attempt to read in the state file header and determine if this | |
701 | is a valid state file. If not, return NULL. | |
702 | ||
703 | Returns a pointer to a properly initialized state file descriptor if | |
704 | successful. */ | |
705 | ||
706 | sfd * | |
707 | sfd_fopen (name, flags) | |
708 | char *name; | |
709 | char *flags; | |
710 | { | |
711 | int success = 0; | |
712 | sfd *asfd; | |
713 | ||
714 | asfd = (sfd *) xmalloc (sizeof (sfd)); | |
715 | (void) memset (asfd, 0, sizeof (sfd)); | |
716 | asfd -> filename = xmalloc (strlen (name) + 1); | |
717 | (void) strcpy (asfd -> filename, name); | |
718 | ||
719 | if ((asfd -> fp = fopen (asfd -> filename, flags)) != NULL) | |
720 | { | |
721 | /* We have the file, now see if we are reading an existing file | |
722 | or writing to a new file. We don't currently support "rw". */ | |
723 | if (strchr (flags, 'r') != NULL) | |
724 | { | |
725 | if (fread ((char *) &asfd -> hdr, sizeof (asfd -> hdr), 1, | |
726 | asfd -> fp) == 1) | |
727 | { | |
728 | if (SF_GOOD_MAGIC (asfd)) | |
729 | { | |
730 | success = 1; | |
731 | } | |
732 | } | |
733 | } | |
734 | else | |
735 | { | |
736 | /* This is a new state file. Initialize various things. */ | |
737 | asfd -> hdr.sf_mag0 = SF_MAG0; | |
738 | asfd -> hdr.sf_mag1 = SF_MAG1; | |
739 | asfd -> hdr.sf_mag2 = SF_MAG2; | |
740 | asfd -> hdr.sf_mag3 = SF_MAG3; | |
741 | success = 1; | |
742 | } | |
743 | } | |
744 | ||
745 | if (!success) | |
746 | { | |
747 | sfd_fclose (asfd); | |
748 | asfd = NULL; | |
749 | } | |
750 | return (asfd); | |
751 | ||
752 | } | |
753 | ||
754 | \f | |
755 | void | |
756 | _initialize_state () | |
757 | { | |
758 | ||
759 | #ifdef HAVE_MMAP | |
760 | ||
761 | add_com ("load-state", class_support, load_state_command, | |
762 | "Load some saved gdb state from FILE.\n\ | |
763 | Select and load some portion of gdb's saved state from the specified file.\n\ | |
764 | The dump-state command may be used to save various portions of gdb's\n\ | |
765 | internal state."); | |
766 | ||
767 | add_com ("dump-state", class_support, dump_state_command, | |
768 | "Dump some of gdb's state to FILE.\n\ | |
769 | Select and dump some portion of gdb's internal state to the specified file.\n\ | |
770 | The load-state command may be used to reload various portions of gdb's\n\ | |
771 | internal state from the file."); | |
772 | ||
773 | #endif /* HAVE_MMAP */ | |
774 | ||
775 | } |