56787a1ca81b684ca550b1e3abba4aba8a94e855
[chise/xemacs-chise.git-] / src / dumper.c
1 /* Portable data dumper for XEmacs.
2    Copyright (C) 1999-2000 Olivier Galibert
3
4 This file is part of XEmacs.
5
6 XEmacs is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
9 later version.
10
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with XEmacs; see the file COPYING.  If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.  */
20
21 /* Synched up with: Not in FSF. */
22
23 #include <config.h>
24 #include "lisp.h"
25
26 #include "dump-id.h"
27 #include "specifier.h"
28 #include "elhash.h"
29 #include "sysfile.h"
30 #include "console-stream.h"
31 #include "dumper.h"
32
33 #ifdef WIN32_NATIVE
34 #include "nt.h"
35 #else
36 #ifdef HAVE_MMAP
37 #include <sys/mman.h>
38 #endif
39 #endif
40
41 #ifndef SEPCHAR
42 #define SEPCHAR ':'
43 #endif
44
45 typedef struct
46 {
47   void *varaddress;
48   size_t size;
49 } pdump_opaque;
50
51 typedef struct
52 {
53   Dynarr_declare (pdump_opaque);
54 } pdump_opaque_dynarr;
55
56 typedef struct
57 {
58   void **ptraddress;
59   const struct struct_description *desc;
60 } pdump_root_struct_ptr;
61
62 typedef struct
63 {
64   Dynarr_declare (pdump_root_struct_ptr);
65 } pdump_root_struct_ptr_dynarr;
66
67 static pdump_opaque_dynarr *pdump_opaques;
68 static pdump_root_struct_ptr_dynarr *pdump_root_struct_ptrs;
69 static Lisp_Object_ptr_dynarr *pdump_root_objects;
70 static Lisp_Object_ptr_dynarr *pdump_weak_object_chains;
71
72 /* Mark SIZE bytes at non-heap address VARADDRESS for dumping as is,
73    without any bit-twiddling. */
74 void
75 dump_add_opaque (void *varaddress, size_t size)
76 {
77   pdump_opaque info;
78   info.varaddress = varaddress;
79   info.size = size;
80   if (pdump_opaques == NULL)
81     pdump_opaques = Dynarr_new (pdump_opaque);
82   Dynarr_add (pdump_opaques, info);
83 }
84
85 /* Mark the struct described by DESC and pointed to by the pointer at
86    non-heap address VARADDRESS for dumping.
87    All the objects reachable from this pointer will also be dumped. */
88 void
89 dump_add_root_struct_ptr (void *ptraddress, const struct struct_description *desc)
90 {
91   pdump_root_struct_ptr info;
92   info.ptraddress = (void **) ptraddress;
93   info.desc = desc;
94   if (pdump_root_struct_ptrs == NULL)
95     pdump_root_struct_ptrs = Dynarr_new (pdump_root_struct_ptr);
96   Dynarr_add (pdump_root_struct_ptrs, info);
97 }
98
99 /* Mark the Lisp_Object at non-heap address VARADDRESS for dumping.
100    All the objects reachable from this var will also be dumped. */
101 void
102 dump_add_root_object (Lisp_Object *varaddress)
103 {
104   if (pdump_root_objects == NULL)
105     pdump_root_objects = Dynarr_new2 (Lisp_Object_ptr_dynarr, Lisp_Object *);
106   Dynarr_add (pdump_root_objects, varaddress);
107 }
108
109 /* Mark the list pointed to by the Lisp_Object at VARADDRESS for dumping. */
110 void
111 dump_add_weak_object_chain (Lisp_Object *varaddress)
112 {
113   if (pdump_weak_object_chains == NULL)
114     pdump_weak_object_chains = Dynarr_new2 (Lisp_Object_ptr_dynarr, Lisp_Object *);
115   Dynarr_add (pdump_weak_object_chains, varaddress);
116 }
117 \f
118
119 typedef struct
120 {
121   const struct lrecord_description *desc;
122   int count;
123 } pdump_reloc_table;
124
125 static char *pdump_rt_list = 0;
126
127 void
128 pdump_objects_unmark (void)
129 {
130   int i;
131   char *p = pdump_rt_list;
132   if (p)
133     for (;;)
134       {
135         pdump_reloc_table *rt = (pdump_reloc_table *)p;
136         p += sizeof (pdump_reloc_table);
137         if (rt->desc)
138           {
139             for (i=0; i<rt->count; i++)
140               {
141                 struct lrecord_header *lh = * (struct lrecord_header **) p;
142                 if (! C_READONLY_RECORD_HEADER_P (lh))
143                   UNMARK_RECORD_HEADER (lh);
144                 p += sizeof (EMACS_INT);
145               }
146           } else
147             break;
148       }
149 }
150
151
152 /* The structure of the file
153  *
154  * 0                    - header
155  * 256                  - dumped objects
156  * stab_offset          - nb_root_struct_ptrs*pair(void *, adr) for pointers to structures
157  *                      - nb_opaques*pair(void *, size) for raw bits to restore
158  *                      - relocation table
159  *                      - wired variable address/value couples with the count preceding the list
160  */
161
162
163 #define PDUMP_SIGNATURE "XEmacsDP"
164 #define PDUMP_SIGNATURE_LEN (sizeof (PDUMP_SIGNATURE) - 1)
165
166 typedef struct
167 {
168   char signature[PDUMP_SIGNATURE_LEN];
169   unsigned int id;
170   EMACS_UINT stab_offset;
171   EMACS_UINT reloc_address;
172   int nb_root_struct_ptrs;
173   int nb_opaques;
174 } pdump_header;
175
176 char *pdump_start, *pdump_end;
177 static size_t pdump_length;
178
179 #ifdef WIN32_NATIVE
180 /* Handle for the dump file */
181 HANDLE pdump_hFile = INVALID_HANDLE_VALUE;
182 /* Handle for the file mapping object for the dump file */
183 HANDLE pdump_hMap = INVALID_HANDLE_VALUE;
184 #endif
185
186 void (*pdump_free) (void);
187
188 static const unsigned char pdump_align_table[256] =
189 {
190   8, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
191   4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
192   5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
193   4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
194   6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
195   4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
196   5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
197   4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
198   7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
199   4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
200   5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
201   4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
202   6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
203   4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
204   5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
205   4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
206 };
207
208 typedef struct pdump_entry_list_elmt
209 {
210   struct pdump_entry_list_elmt *next;
211   const void *obj;
212   size_t size;
213   int count;
214   int is_lrecord;
215   EMACS_INT save_offset;
216 } pdump_entry_list_elmt;
217
218 typedef struct
219 {
220   pdump_entry_list_elmt *first;
221   int align;
222   int count;
223 } pdump_entry_list;
224
225 typedef struct pdump_struct_list_elmt
226 {
227   pdump_entry_list list;
228   const struct struct_description *sdesc;
229 } pdump_struct_list_elmt;
230
231 typedef struct
232 {
233   pdump_struct_list_elmt *list;
234   int count;
235   int size;
236 } pdump_struct_list;
237
238 static pdump_entry_list pdump_object_table[256];
239 static pdump_entry_list pdump_opaque_data_list;
240 static pdump_struct_list pdump_struct_table;
241
242 static int pdump_alert_undump_object[256];
243
244 static unsigned long cur_offset;
245 static size_t max_size;
246 static int pdump_fd;
247 static void *pdump_buf;
248
249 #ifdef UTF2000
250 #define PDUMP_HASHSIZE 20000001
251 #else
252 #define PDUMP_HASHSIZE 200001
253 #endif
254
255 static pdump_entry_list_elmt **pdump_hash;
256
257 /* Since most pointers are eight bytes aligned, the >>3 allows for a better hash */
258 static int
259 pdump_make_hash (const void *obj)
260 {
261   return ((unsigned long)(obj)>>3) % PDUMP_HASHSIZE;
262 }
263
264 static pdump_entry_list_elmt *
265 pdump_get_entry (const void *obj)
266 {
267   int pos = pdump_make_hash (obj);
268   pdump_entry_list_elmt *e;
269
270   assert (obj != 0);
271
272   while ((e = pdump_hash[pos]) != 0)
273     {
274       if (e->obj == obj)
275         return e;
276
277       pos++;
278       if (pos == PDUMP_HASHSIZE)
279         pos = 0;
280     }
281   return 0;
282 }
283
284 static void
285 pdump_add_entry (pdump_entry_list *list, const void *obj, size_t size, int count, int is_lrecord)
286 {
287   pdump_entry_list_elmt *e;
288   int align;
289   int pos = pdump_make_hash (obj);
290
291   while ((e = pdump_hash[pos]) != 0)
292     {
293       if (e->obj == obj)
294         return;
295
296       pos++;
297       if (pos == PDUMP_HASHSIZE)
298         pos = 0;
299     }
300
301   e = xnew (pdump_entry_list_elmt);
302
303   e->next = list->first;
304   e->obj = obj;
305   e->size = size;
306   e->count = count;
307   e->is_lrecord = is_lrecord;
308   list->first = e;
309
310   list->count += count;
311   pdump_hash[pos] = e;
312
313   align = pdump_align_table[size & 255];
314   if (align < 2 && is_lrecord)
315     align = 2;
316
317   if (align < list->align)
318     list->align = align;
319 }
320
321 static pdump_entry_list *
322 pdump_get_entry_list (const struct struct_description *sdesc)
323 {
324   int i;
325   for (i=0; i<pdump_struct_table.count; i++)
326     if (pdump_struct_table.list[i].sdesc == sdesc)
327       return &pdump_struct_table.list[i].list;
328
329   if (pdump_struct_table.size <= pdump_struct_table.count)
330     {
331       if (pdump_struct_table.size == -1)
332         pdump_struct_table.size = 10;
333       else
334         pdump_struct_table.size = pdump_struct_table.size * 2;
335       pdump_struct_table.list = (pdump_struct_list_elmt *)
336         xrealloc (pdump_struct_table.list,
337                   pdump_struct_table.size * sizeof (pdump_struct_list_elmt));
338     }
339   pdump_struct_table.list[pdump_struct_table.count].list.first = 0;
340   pdump_struct_table.list[pdump_struct_table.count].list.align = 8;
341   pdump_struct_table.list[pdump_struct_table.count].list.count = 0;
342   pdump_struct_table.list[pdump_struct_table.count].sdesc = sdesc;
343
344   return &pdump_struct_table.list[pdump_struct_table.count++].list;
345 }
346
347 static struct
348 {
349   struct lrecord_header *obj;
350   int position;
351   int offset;
352 } backtrace[65536];
353
354 static int depth;
355
356 static void
357 pdump_backtrace (void)
358 {
359   int i;
360   stderr_out ("pdump backtrace :\n");
361   for (i=0;i<depth;i++)
362     {
363       if (!backtrace[i].obj)
364         stderr_out ("  - ind. (%d, %d)\n", backtrace[i].position, backtrace[i].offset);
365       else
366         {
367           stderr_out ("  - %s (%d, %d)\n",
368                    LHEADER_IMPLEMENTATION (backtrace[i].obj)->name,
369                    backtrace[i].position,
370                    backtrace[i].offset);
371         }
372     }
373 }
374
375 static void pdump_register_object (Lisp_Object obj);
376 static void pdump_register_struct (const void *data, const struct struct_description *sdesc, int count);
377
378 static EMACS_INT
379 pdump_get_indirect_count (EMACS_INT code, const struct lrecord_description *idesc, const void *idata)
380 {
381   EMACS_INT count;
382   const void *irdata;
383
384   int line = XD_INDIRECT_VAL (code);
385   int delta = XD_INDIRECT_DELTA (code);
386
387   irdata = ((char *)idata) + idesc[line].offset;
388   switch (idesc[line].type)
389     {
390     case XD_SIZE_T:
391       count = *(size_t *)irdata;
392       break;
393     case XD_INT:
394       count = *(int *)irdata;
395       break;
396     case XD_LONG:
397       count = *(long *)irdata;
398       break;
399     case XD_BYTECOUNT:
400       count = *(Bytecount *)irdata;
401       break;
402     default:
403       stderr_out ("Unsupported count type : %d (line = %d, code=%ld)\n", idesc[line].type, line, (long)code);
404       pdump_backtrace ();
405       abort ();
406     }
407   count += delta;
408   return count;
409 }
410
411 static void
412 pdump_register_sub (const void *data, const struct lrecord_description *desc, int me)
413 {
414   int pos;
415
416  restart:
417   for (pos = 0; desc[pos].type != XD_END; pos++)
418     {
419       const void *rdata = (const char *)data + desc[pos].offset;
420
421       backtrace[me].position = pos;
422       backtrace[me].offset = desc[pos].offset;
423
424       switch (desc[pos].type)
425         {
426         case XD_SPECIFIER_END:
427           pos = 0;
428           desc = ((const Lisp_Specifier *)data)->methods->extra_description;
429           goto restart;
430         case XD_SIZE_T:
431         case XD_INT:
432         case XD_LONG:
433         case XD_BYTECOUNT:
434         case XD_INT_RESET:
435         case XD_LO_LINK:
436           break;
437         case XD_OPAQUE_DATA_PTR:
438           {
439             EMACS_INT count = desc[pos].data1;
440             if (XD_IS_INDIRECT (count))
441               count = pdump_get_indirect_count (count, desc, data);
442
443             pdump_add_entry (&pdump_opaque_data_list,
444                              *(void **)rdata,
445                              count,
446                              1,
447                              0);
448             break;
449           }
450         case XD_C_STRING:
451           {
452             const char *str = *(const char **)rdata;
453             if (str)
454               pdump_add_entry (&pdump_opaque_data_list, str, strlen (str)+1, 1, 0);
455             break;
456           }
457         case XD_DOC_STRING:
458           {
459             const char *str = *(const char **)rdata;
460             if ((EMACS_INT)str > 0)
461               pdump_add_entry (&pdump_opaque_data_list, str, strlen (str)+1, 1, 0);
462             break;
463           }
464         case XD_LISP_OBJECT:
465           {
466             const Lisp_Object *pobj = (const Lisp_Object *)rdata;
467
468             assert (desc[pos].data1 == 0);
469
470             backtrace[me].offset = (const char *)pobj - (const char *)data;
471             pdump_register_object (*pobj);
472             break;
473           }
474         case XD_LISP_OBJECT_ARRAY:
475           {
476             int i;
477             EMACS_INT count = desc[pos].data1;
478             if (XD_IS_INDIRECT (count))
479               count = pdump_get_indirect_count (count, desc, data);
480
481             for (i = 0; i < count; i++)
482               {
483                 const Lisp_Object *pobj = ((const Lisp_Object *)rdata) + i;
484                 Lisp_Object dobj = *pobj;
485
486                 backtrace[me].offset = (const char *)pobj - (const char *)data;
487                 pdump_register_object (dobj);
488               }
489             break;
490           }
491         case XD_STRUCT_PTR:
492           {
493             EMACS_INT count = desc[pos].data1;
494             const struct struct_description *sdesc = desc[pos].data2;
495             const char *dobj = *(const char **)rdata;
496             if (dobj)
497               {
498                 if (XD_IS_INDIRECT (count))
499                   count = pdump_get_indirect_count (count, desc, data);
500
501                 pdump_register_struct (dobj, sdesc, count);
502               }
503             break;
504           }
505         default:
506           stderr_out ("Unsupported dump type : %d\n", desc[pos].type);
507           pdump_backtrace ();
508           abort ();
509         };
510     }
511 }
512
513 static void
514 pdump_register_object (Lisp_Object obj)
515 {
516   struct lrecord_header *objh;
517
518   if (!POINTER_TYPE_P (XTYPE (obj)))
519     return;
520
521   objh = XRECORD_LHEADER (obj);
522   if (!objh)
523     return;
524
525   if (pdump_get_entry (objh))
526     return;
527
528   if (LHEADER_IMPLEMENTATION (objh)->description)
529     {
530       int me = depth++;
531       if (me>65536)
532         {
533           stderr_out ("Backtrace overflow, loop ?\n");
534           abort ();
535         }
536       backtrace[me].obj = objh;
537       backtrace[me].position = 0;
538       backtrace[me].offset = 0;
539
540       pdump_add_entry (pdump_object_table + objh->type,
541                        objh,
542                        LHEADER_IMPLEMENTATION (objh)->static_size ?
543                        LHEADER_IMPLEMENTATION (objh)->static_size :
544                        LHEADER_IMPLEMENTATION (objh)->size_in_bytes_method (objh),
545                        1,
546                        1);
547       pdump_register_sub (objh,
548                           LHEADER_IMPLEMENTATION (objh)->description,
549                           me);
550       --depth;
551     }
552   else
553     {
554       pdump_alert_undump_object[objh->type]++;
555       stderr_out ("Undumpable object type : %s\n", LHEADER_IMPLEMENTATION (objh)->name);
556       pdump_backtrace ();
557     }
558 }
559
560 static void
561 pdump_register_struct (const void *data, const struct struct_description *sdesc, int count)
562 {
563   if (data && !pdump_get_entry (data))
564     {
565       int me = depth++;
566       int i;
567       if (me>65536)
568         {
569           stderr_out ("Backtrace overflow, loop ?\n");
570           abort ();
571         }
572       backtrace[me].obj = 0;
573       backtrace[me].position = 0;
574       backtrace[me].offset = 0;
575
576       pdump_add_entry (pdump_get_entry_list (sdesc),
577                        data,
578                        sdesc->size,
579                        count,
580                        0);
581       for (i=0; i<count; i++)
582         {
583           pdump_register_sub (((char *)data) + sdesc->size*i,
584                               sdesc->description,
585                               me);
586         }
587       --depth;
588     }
589 }
590
591 static void
592 pdump_dump_data (pdump_entry_list_elmt *elmt, const struct lrecord_description *desc)
593 {
594   size_t size = elmt->size;
595   int count = elmt->count;
596   if (desc)
597     {
598       int pos, i;
599       memcpy (pdump_buf, elmt->obj, size*count);
600
601       for (i=0; i<count; i++)
602         {
603           char *cur = ((char *)pdump_buf) + i*size;
604         restart:
605           for (pos = 0; desc[pos].type != XD_END; pos++)
606             {
607               void *rdata = cur + desc[pos].offset;
608               switch (desc[pos].type)
609                 {
610                 case XD_SPECIFIER_END:
611                   desc = ((const Lisp_Specifier *)(elmt->obj))->methods->extra_description;
612                   goto restart;
613                 case XD_SIZE_T:
614                 case XD_INT:
615                 case XD_LONG:
616                 case XD_BYTECOUNT:
617                   break;
618                 case XD_INT_RESET:
619                   {
620                     EMACS_INT val = desc[pos].data1;
621                     if (XD_IS_INDIRECT (val))
622                       val = pdump_get_indirect_count (val, desc, elmt->obj);
623                     *(int *)rdata = val;
624                     break;
625                   }
626                 case XD_OPAQUE_DATA_PTR:
627                 case XD_C_STRING:
628                 case XD_STRUCT_PTR:
629                   {
630                     void *ptr = *(void **)rdata;
631                     if (ptr)
632                       *(EMACS_INT *)rdata = pdump_get_entry (ptr)->save_offset;
633                     break;
634                   }
635                 case XD_LO_LINK:
636                   {
637                     Lisp_Object obj = *(Lisp_Object *)rdata;
638                     pdump_entry_list_elmt *elmt1;
639                     for (;;)
640                       {
641                         elmt1 = pdump_get_entry (XRECORD_LHEADER (obj));
642                         if (elmt1)
643                           break;
644                         obj = *(Lisp_Object *)(desc[pos].offset + (char *)(XRECORD_LHEADER (obj)));
645                       }
646                     *(EMACS_INT *)rdata = elmt1->save_offset;
647                     break;
648                   }
649                 case XD_LISP_OBJECT:
650                   {
651                     Lisp_Object *pobj = (Lisp_Object *) rdata;
652
653                     assert (desc[pos].data1 == 0);
654
655                     if (POINTER_TYPE_P (XTYPE (*pobj)) && XRECORD_LHEADER (*pobj))
656                       *(EMACS_INT *)pobj =
657                         pdump_get_entry (XRECORD_LHEADER (*pobj))->save_offset;
658                     break;
659                   }
660                 case XD_LISP_OBJECT_ARRAY:
661                   {
662                     EMACS_INT num = desc[pos].data1;
663                     int j;
664                     if (XD_IS_INDIRECT (num))
665                       num = pdump_get_indirect_count (num, desc, elmt->obj);
666
667                     for (j=0; j<num; j++)
668                       {
669                         Lisp_Object *pobj = ((Lisp_Object *)rdata) + j;
670                         if (POINTER_TYPE_P (XTYPE (*pobj)) && XRECORD_LHEADER (*pobj))
671                           *(EMACS_INT *)pobj =
672                             pdump_get_entry (XRECORD_LHEADER (*pobj))->save_offset;
673                       }
674                     break;
675                   }
676                 case XD_DOC_STRING:
677                   {
678                     EMACS_INT str = *(EMACS_INT *)rdata;
679                     if (str > 0)
680                       *(EMACS_INT *)rdata = pdump_get_entry ((void *)str)->save_offset;
681                     break;
682                   }
683                 default:
684                   stderr_out ("Unsupported dump type : %d\n", desc[pos].type);
685                   abort ();
686                 };
687             }
688         }
689     }
690   write (pdump_fd, desc ? pdump_buf : elmt->obj, size*count);
691   if (elmt->is_lrecord && ((size*count) & 3))
692     write (pdump_fd, "\0\0\0", 4-((size*count) & 3));
693 }
694
695 static void
696 pdump_reloc_one (void *data, EMACS_INT delta, const struct lrecord_description *desc)
697 {
698   int pos;
699
700  restart:
701   for (pos = 0; desc[pos].type != XD_END; pos++)
702     {
703       void *rdata = (char *)data + desc[pos].offset;
704       switch (desc[pos].type)
705         {
706         case XD_SPECIFIER_END:
707           pos = 0;
708           desc = ((const Lisp_Specifier *)data)->methods->extra_description;
709           goto restart;
710         case XD_SIZE_T:
711         case XD_INT:
712         case XD_LONG:
713         case XD_BYTECOUNT:
714         case XD_INT_RESET:
715           break;
716         case XD_OPAQUE_DATA_PTR:
717         case XD_C_STRING:
718         case XD_STRUCT_PTR:
719         case XD_LO_LINK:
720           {
721             EMACS_INT ptr = *(EMACS_INT *)rdata;
722             if (ptr)
723               *(EMACS_INT *)rdata = ptr+delta;
724             break;
725           }
726         case XD_LISP_OBJECT:
727           {
728             Lisp_Object *pobj = (Lisp_Object *) rdata;
729
730             assert (desc[pos].data1 == 0);
731
732             if (POINTER_TYPE_P (XTYPE (*pobj))
733                 && ! EQ (*pobj, Qnull_pointer))
734               XSETOBJ (*pobj, (char *) XPNTR (*pobj) + delta);
735
736             break;
737           }
738         case XD_LISP_OBJECT_ARRAY:
739           {
740             EMACS_INT num = desc[pos].data1;
741             int j;
742             if (XD_IS_INDIRECT (num))
743               num = pdump_get_indirect_count (num, desc, data);
744
745             for (j=0; j<num; j++)
746               {
747                 Lisp_Object *pobj = (Lisp_Object *) rdata + j;
748
749                 if (POINTER_TYPE_P (XTYPE (*pobj))
750                     && ! EQ (*pobj, Qnull_pointer))
751                   XSETOBJ (*pobj, (char *) XPNTR (*pobj) + delta);
752               }
753             break;
754           }
755         case XD_DOC_STRING:
756           {
757             EMACS_INT str = *(EMACS_INT *)rdata;
758             if (str > 0)
759               *(EMACS_INT *)rdata = str + delta;
760             break;
761           }
762         default:
763           stderr_out ("Unsupported dump type : %d\n", desc[pos].type);
764           abort ();
765         };
766     }
767 }
768
769 static void
770 pdump_allocate_offset (pdump_entry_list_elmt *elmt, const struct lrecord_description *desc)
771 {
772   size_t size = (elmt->is_lrecord ? (elmt->size + 3) & ~3 : elmt->size)*elmt->count;
773   elmt->save_offset = cur_offset;
774   if (size>max_size)
775     max_size = size;
776   cur_offset += size;
777 }
778
779 static void
780 pdump_scan_by_alignment (void (*f)(pdump_entry_list_elmt *, const struct lrecord_description *))
781 {
782   int align, i;
783   const struct lrecord_description *idesc;
784   pdump_entry_list_elmt *elmt;
785   for (align=8; align>=0; align--)
786     {
787       for (i=0; i<lrecord_type_count; i++)
788         if (pdump_object_table[i].align == align)
789           {
790             elmt = pdump_object_table[i].first;
791             if (!elmt)
792               continue;
793             idesc = lrecord_implementations_table[i]->description;
794             while (elmt)
795               {
796                 f (elmt, idesc);
797                 elmt = elmt->next;
798               }
799           }
800
801       for (i=0; i<pdump_struct_table.count; i++)
802         if (pdump_struct_table.list[i].list.align == align)
803           {
804             elmt = pdump_struct_table.list[i].list.first;
805             idesc = pdump_struct_table.list[i].sdesc->description;
806             while (elmt)
807               {
808                 f (elmt, idesc);
809                 elmt = elmt->next;
810               }
811           }
812
813       elmt = pdump_opaque_data_list.first;
814       while (elmt)
815         {
816           if (pdump_align_table[elmt->size & 255] == align)
817             f (elmt, 0);
818           elmt = elmt->next;
819         }
820     }
821 }
822
823 static void
824 pdump_dump_from_root_struct_ptrs (void)
825 {
826   int i;
827   for (i = 0; i < Dynarr_length (pdump_root_struct_ptrs); i++)
828     {
829       EMACS_INT adr;
830       pdump_root_struct_ptr *info = Dynarr_atp (pdump_root_struct_ptrs, i);
831       write (pdump_fd, &info->ptraddress, sizeof (info->ptraddress));
832       adr = pdump_get_entry (*(info->ptraddress))->save_offset;
833       write (pdump_fd, &adr, sizeof (adr));
834     }
835 }
836
837 static void
838 pdump_dump_opaques (void)
839 {
840   int i;
841   for (i = 0; i < Dynarr_length (pdump_opaques); i++)
842     {
843       pdump_opaque *info = Dynarr_atp (pdump_opaques, i);
844       write (pdump_fd, info, sizeof (*info));
845       write (pdump_fd, info->varaddress, info->size);
846     }
847 }
848
849 static void
850 pdump_dump_rtables (void)
851 {
852   int i;
853   pdump_entry_list_elmt *elmt;
854   pdump_reloc_table rt;
855
856   for (i=0; i<lrecord_type_count; i++)
857     {
858       elmt = pdump_object_table[i].first;
859       if (!elmt)
860         continue;
861       rt.desc = lrecord_implementations_table[i]->description;
862       rt.count = pdump_object_table[i].count;
863       write (pdump_fd, &rt, sizeof (rt));
864       while (elmt)
865         {
866           EMACS_INT rdata = pdump_get_entry (elmt->obj)->save_offset;
867           write (pdump_fd, &rdata, sizeof (rdata));
868           elmt = elmt->next;
869         }
870     }
871
872   rt.desc = 0;
873   rt.count = 0;
874   write (pdump_fd, &rt, sizeof (rt));
875
876   for (i=0; i<pdump_struct_table.count; i++)
877     {
878       elmt = pdump_struct_table.list[i].list.first;
879       rt.desc = pdump_struct_table.list[i].sdesc->description;
880       rt.count = pdump_struct_table.list[i].list.count;
881       write (pdump_fd, &rt, sizeof (rt));
882       while (elmt)
883         {
884           EMACS_INT rdata = pdump_get_entry (elmt->obj)->save_offset;
885           int j;
886           for (j=0; j<elmt->count; j++)
887             {
888               write (pdump_fd, &rdata, sizeof (rdata));
889               rdata += elmt->size;
890             }
891           elmt = elmt->next;
892         }
893     }
894   rt.desc = 0;
895   rt.count = 0;
896   write (pdump_fd, &rt, sizeof (rt));
897 }
898
899 static void
900 pdump_dump_from_root_objects (void)
901 {
902   size_t count = Dynarr_length (pdump_root_objects) + Dynarr_length (pdump_weak_object_chains);
903   size_t i;
904
905   write (pdump_fd, &count, sizeof (count));
906
907   for (i=0; i<Dynarr_length (pdump_root_objects); i++)
908     {
909       Lisp_Object obj = * Dynarr_at (pdump_root_objects, i);
910       if (POINTER_TYPE_P (XTYPE (obj)))
911         obj = wrap_object ((void *) pdump_get_entry (XRECORD_LHEADER (obj))->save_offset);
912       write (pdump_fd, Dynarr_atp (pdump_root_objects, i), sizeof (Dynarr_atp (pdump_root_objects, i)));
913       write (pdump_fd, &obj, sizeof (obj));
914     }
915
916   for (i=0; i<Dynarr_length (pdump_weak_object_chains); i++)
917     {
918       Lisp_Object obj = * Dynarr_at (pdump_weak_object_chains, i);
919       pdump_entry_list_elmt *elmt;
920
921       for (;;)
922         {
923           const struct lrecord_description *desc;
924           int pos;
925           elmt = pdump_get_entry (XRECORD_LHEADER (obj));
926           if (elmt)
927             break;
928           desc = XRECORD_LHEADER_IMPLEMENTATION (obj)->description;
929           for (pos = 0; desc[pos].type != XD_LO_LINK; pos++)
930             assert (desc[pos].type != XD_END);
931
932           obj = *(Lisp_Object *)(desc[pos].offset + (char *)(XRECORD_LHEADER (obj)));
933         }
934       obj = wrap_object ((void *) elmt->save_offset);
935
936       write (pdump_fd, Dynarr_atp (pdump_weak_object_chains, i), sizeof (Lisp_Object *));
937       write (pdump_fd, &obj, sizeof (obj));
938     }
939 }
940
941 void
942 pdump (void)
943 {
944   int i;
945   Lisp_Object t_console, t_device, t_frame;
946   int none;
947   pdump_header hd;
948
949   flush_all_buffer_local_cache ();
950
951   /* These appear in a DEFVAR_LISP, which does a staticpro() */
952   t_console = Vterminal_console; Vterminal_console = Qnil;
953   t_frame   = Vterminal_frame;   Vterminal_frame   = Qnil;
954   t_device  = Vterminal_device;  Vterminal_device  = Qnil;
955
956   dump_add_opaque (&lrecord_implementations_table,
957                    lrecord_type_count * sizeof (lrecord_implementations_table[0]));
958   dump_add_opaque (&lrecord_markers,
959                    lrecord_type_count * sizeof (lrecord_markers[0]));
960
961   pdump_hash = xnew_array_and_zero (pdump_entry_list_elmt *, PDUMP_HASHSIZE);
962
963   for (i=0; i<lrecord_type_count; i++)
964     {
965       pdump_object_table[i].first = 0;
966       pdump_object_table[i].align = 8;
967       pdump_object_table[i].count = 0;
968       pdump_alert_undump_object[i] = 0;
969     }
970   pdump_struct_table.count = 0;
971   pdump_struct_table.size = -1;
972
973   pdump_opaque_data_list.first = 0;
974   pdump_opaque_data_list.align = 8;
975   pdump_opaque_data_list.count = 0;
976   depth = 0;
977
978   for (i=0; i<Dynarr_length (pdump_root_objects); i++)
979     pdump_register_object (* Dynarr_at (pdump_root_objects, i));
980
981   none = 1;
982   for (i=0; i<lrecord_type_count; i++)
983     if (pdump_alert_undump_object[i])
984       {
985         if (none)
986           printf ("Undumpable types list :\n");
987         none = 0;
988         printf ("  - %s (%d)\n", lrecord_implementations_table[i]->name, pdump_alert_undump_object[i]);
989       }
990   if (!none)
991     return;
992
993   for (i=0; i<Dynarr_length (pdump_root_struct_ptrs); i++)
994     {
995       pdump_root_struct_ptr info = Dynarr_at (pdump_root_struct_ptrs, i);
996       pdump_register_struct (*(info.ptraddress), info.desc, 1);
997     }
998
999   memcpy (hd.signature, PDUMP_SIGNATURE, PDUMP_SIGNATURE_LEN);
1000   hd.id = dump_id;
1001   hd.reloc_address = 0;
1002   hd.nb_root_struct_ptrs = Dynarr_length (pdump_root_struct_ptrs);
1003   hd.nb_opaques = Dynarr_length (pdump_opaques);
1004
1005   cur_offset = 256;
1006   max_size = 0;
1007
1008   pdump_scan_by_alignment (pdump_allocate_offset);
1009
1010   pdump_buf = xmalloc (max_size);
1011   /* Avoid use of the `open' macro.  We want the real function. */
1012 #undef open
1013   pdump_fd = open (EMACS_PROGNAME ".dmp",
1014                    O_WRONLY | O_CREAT | O_TRUNC | OPEN_BINARY, 0666);
1015   hd.stab_offset = (cur_offset + 3) & ~3;
1016
1017   write (pdump_fd, &hd, sizeof (hd));
1018   lseek (pdump_fd, 256, SEEK_SET);
1019
1020   pdump_scan_by_alignment (pdump_dump_data);
1021
1022   lseek (pdump_fd, hd.stab_offset, SEEK_SET);
1023
1024   pdump_dump_from_root_struct_ptrs ();
1025   pdump_dump_opaques ();
1026   pdump_dump_rtables ();
1027   pdump_dump_from_root_objects ();
1028
1029   close (pdump_fd);
1030   free (pdump_buf);
1031
1032   free (pdump_hash);
1033
1034   Vterminal_console = t_console;
1035   Vterminal_frame   = t_frame;
1036   Vterminal_device  = t_device;
1037 }
1038
1039 static int
1040 pdump_load_check (void)
1041 {
1042   return (!memcmp (((pdump_header *)pdump_start)->signature,
1043                    PDUMP_SIGNATURE, PDUMP_SIGNATURE_LEN)
1044           && ((pdump_header *)pdump_start)->id == dump_id);
1045 }
1046
1047 static int
1048 pdump_load_finish (void)
1049 {
1050   int i;
1051   char *p;
1052   EMACS_INT delta;
1053   EMACS_INT count;
1054
1055   pdump_end = pdump_start + pdump_length;
1056
1057 #define PDUMP_READ(p, type) (p = (char*) (((type *) p) + 1), *((type *) p - 1))
1058
1059   delta = ((EMACS_INT)pdump_start) - ((pdump_header *)pdump_start)->reloc_address;
1060   p = pdump_start + ((pdump_header *)pdump_start)->stab_offset;
1061
1062   /* Put back the pdump_root_struct_ptrs */
1063   for (i=0; i<((pdump_header *)pdump_start)->nb_root_struct_ptrs; i++)
1064     {
1065       void **adr = PDUMP_READ (p, void **);
1066       *adr = (void *) (PDUMP_READ (p, char *) + delta);
1067     }
1068
1069   /* Put back the pdump_opaques */
1070   for (i=0; i<((pdump_header *)pdump_start)->nb_opaques; i++)
1071     {
1072       pdump_opaque info = PDUMP_READ (p, pdump_opaque);
1073       memcpy (info.varaddress, p, info.size);
1074       p += info.size;
1075     }
1076
1077   /* Do the relocations */
1078   pdump_rt_list = p;
1079   count = 2;
1080   for (;;)
1081     {
1082       pdump_reloc_table rt = PDUMP_READ (p, pdump_reloc_table);
1083       if (rt.desc)
1084         {
1085           for (i=0; i < rt.count; i++)
1086             {
1087               char *adr = delta + *(char **)p;
1088               *(char **)p = adr;
1089               pdump_reloc_one (adr, delta, rt.desc);
1090               p += sizeof (char *);
1091             }
1092         } else
1093           if (!(--count))
1094             break;
1095     }
1096
1097   /* Put the pdump_root_objects variables in place */
1098   for (i = PDUMP_READ (p, size_t); i; i--)
1099     {
1100       Lisp_Object *var = PDUMP_READ (p, Lisp_Object *);
1101       Lisp_Object  obj = PDUMP_READ (p, Lisp_Object);
1102
1103       if (POINTER_TYPE_P (XTYPE (obj)))
1104         obj = wrap_object ((char *) XPNTR (obj) + delta);
1105
1106       *var = obj;
1107     }
1108
1109   /* Final cleanups */
1110   /*   reorganize hash tables */
1111   p = pdump_rt_list;
1112   for (;;)
1113     {
1114       pdump_reloc_table rt = PDUMP_READ (p, pdump_reloc_table);
1115       if (!rt.desc)
1116         break;
1117       if (rt.desc == hash_table_description)
1118         {
1119           for (i=0; i < rt.count; i++)
1120             pdump_reorganize_hash_table (PDUMP_READ (p, Lisp_Object));
1121           break;
1122         } else
1123           p += sizeof (Lisp_Object) * rt.count;
1124     }
1125
1126   return 1;
1127 }
1128
1129 #ifdef WIN32_NATIVE
1130 /* Free the mapped file if we decide we don't want it after all */
1131 static void
1132 pdump_file_unmap (void)
1133 {
1134   UnmapViewOfFile (pdump_start);
1135   CloseHandle (pdump_hFile);
1136   CloseHandle (pdump_hMap);
1137 }
1138
1139 static int
1140 pdump_file_get (const char *path)
1141 {
1142
1143   pdump_hFile = CreateFile (path,
1144                             GENERIC_READ + GENERIC_WRITE,  /* Required for copy on write */
1145                             0,                      /* Not shared */
1146                             NULL,                   /* Not inheritable */
1147                             OPEN_EXISTING,
1148                             FILE_ATTRIBUTE_NORMAL,
1149                             NULL);                  /* No template file */
1150   if (pdump_hFile == INVALID_HANDLE_VALUE)
1151     return 0;
1152
1153   pdump_length = GetFileSize (pdump_hFile, NULL);
1154   pdump_hMap = CreateFileMapping (pdump_hFile,
1155                                   NULL,             /* No security attributes */
1156                                   PAGE_WRITECOPY,   /* Copy on write */
1157                                   0,                /* Max size, high half */
1158                                   0,                /* Max size, low half */
1159                                   NULL);            /* Unnamed */
1160   if (pdump_hMap == INVALID_HANDLE_VALUE)
1161     return 0;
1162
1163   pdump_start = MapViewOfFile (pdump_hMap,
1164                                FILE_MAP_COPY, /* Copy on write */
1165                                0,             /* Start at zero */
1166                                0,
1167                                0);            /* Map all of it */
1168   pdump_free = pdump_file_unmap;
1169   return 1;
1170 }
1171
1172 /* pdump_resource_free is called (via the pdump_free pointer) to release
1173    any resources allocated by pdump_resource_get.  Since the Windows API
1174    specs specifically state that you don't need to (and shouldn't) free the
1175    resources allocated by FindResource, LoadResource, and LockResource this
1176    routine does nothing.  */
1177 static void
1178 pdump_resource_free (void)
1179 {
1180 }
1181
1182 static int
1183 pdump_resource_get (void)
1184 {
1185   HRSRC hRes;                   /* Handle to dump resource */
1186   HRSRC hResLoad;               /* Handle to loaded dump resource */
1187
1188   /* See Q126630 which describes how Windows NT and 95 trap writes to
1189      resource sections and duplicate the page to allow the write to proceed.
1190      It also describes how to make the resource section read/write (and hence
1191      private to each process).  Doing this avoids the exceptions and related
1192      overhead, but causes the resource section to be private to each process
1193      that is running XEmacs.  Since the resource section contains little
1194      other than the dumped data, which should be private to each process, we
1195      make the whole resource section read/write so we don't have to copy it. */
1196
1197   hRes = FindResource (NULL, MAKEINTRESOURCE(101), "DUMP");
1198   if (hRes == NULL)
1199     return 0;
1200
1201   /* Found it, use the data in the resource */
1202   hResLoad = LoadResource (NULL, hRes);
1203   if (hResLoad == NULL)
1204     return 0;
1205
1206   pdump_start = LockResource (hResLoad);
1207   if (pdump_start == NULL)
1208     return 0;
1209
1210   pdump_free = pdump_resource_free;
1211   pdump_length = SizeofResource (NULL, hRes);
1212   if (pdump_length <= sizeof (pdump_header))
1213     {
1214       pdump_start = 0;
1215       return 0;
1216     }
1217
1218   return 1;
1219 }
1220
1221 #else /* !WIN32_NATIVE */
1222
1223 static void *pdump_mallocadr;
1224
1225 static void
1226 pdump_file_free (void)
1227 {
1228   xfree (pdump_mallocadr);
1229 }
1230
1231 #ifdef HAVE_MMAP
1232 static void
1233 pdump_file_unmap (void)
1234 {
1235   munmap (pdump_start, pdump_length);
1236 }
1237 #endif
1238
1239 static int
1240 pdump_file_get (const char *path)
1241 {
1242   int fd = open (path, O_RDONLY | OPEN_BINARY);
1243   if (fd<0)
1244     return 0;
1245
1246   pdump_length = lseek (fd, 0, SEEK_END);
1247   if (pdump_length < sizeof (pdump_header))
1248     {
1249       close (fd);
1250       return 0;
1251     }
1252
1253   lseek (fd, 0, SEEK_SET);
1254
1255 #ifdef HAVE_MMAP
1256 /* Unix 98 requires that sys/mman.h define MAP_FAILED,
1257    but many earlier implementations don't. */
1258 # ifndef MAP_FAILED
1259 #  define MAP_FAILED ((void *) -1L)
1260 # endif
1261   pdump_start = (char *) mmap (0, pdump_length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
1262   if (pdump_start != (char *) MAP_FAILED)
1263     {
1264       pdump_free = pdump_file_unmap;
1265       close (fd);
1266       return 1;
1267     }
1268 #endif /* HAVE_MMAP */
1269
1270   pdump_mallocadr = xmalloc (pdump_length+255);
1271   pdump_free = pdump_file_free;
1272   pdump_start = (char *)((255 + (unsigned long)pdump_mallocadr) & ~255);
1273   read (fd, pdump_start, pdump_length);
1274
1275   close (fd);
1276   return 1;
1277 }
1278 #endif /* !WIN32_NATIVE */
1279
1280
1281 static int
1282 pdump_file_try (char *exe_path)
1283 {
1284   char *w;
1285
1286   w = exe_path + strlen (exe_path);
1287   do
1288     {
1289       sprintf (w, "-%s-%08x.dmp", EMACS_VERSION, dump_id);
1290       if (pdump_file_get (exe_path))
1291         {
1292           if (pdump_load_check ())
1293             return 1;
1294           pdump_free ();
1295         }
1296
1297       sprintf (w, "-%08x.dmp", dump_id);
1298       if (pdump_file_get (exe_path))
1299         {
1300           if (pdump_load_check ())
1301             return 1;
1302           pdump_free ();
1303         }
1304
1305       sprintf (w, ".dmp");
1306       if (pdump_file_get (exe_path))
1307         {
1308           if (pdump_load_check ())
1309             return 1;
1310           pdump_free ();
1311         }
1312
1313       do
1314         w--;
1315       while (w>exe_path && !IS_DIRECTORY_SEP (*w) && (*w != '-') && (*w != '.'));
1316     }
1317   while (w>exe_path && !IS_DIRECTORY_SEP (*w));
1318   return 0;
1319 }
1320
1321 int
1322 pdump_load (const char *argv0)
1323 {
1324   char exe_path[PATH_MAX];
1325 #ifdef WIN32_NATIVE
1326   GetModuleFileName (NULL, exe_path, PATH_MAX);
1327 #else /* !WIN32_NATIVE */
1328   char *w;
1329   const char *dir, *p;
1330
1331   dir = argv0;
1332   if (dir[0] == '-')
1333     {
1334       /* XEmacs as a login shell, oh goody! */
1335       dir = getenv ("SHELL");
1336     }
1337
1338   p = dir + strlen (dir);
1339   while (p != dir && !IS_ANY_SEP (p[-1])) p--;
1340
1341   if (p != dir)
1342     {
1343       /* invocation-name includes a directory component -- presumably it
1344          is relative to cwd, not $PATH */
1345       strcpy (exe_path, dir);
1346     }
1347   else
1348     {
1349       const char *path = getenv ("PATH");
1350       const char *name = p;
1351       for (;;)
1352         {
1353           p = path;
1354           while (*p && *p != SEPCHAR)
1355             p++;
1356           if (p == path)
1357             {
1358               exe_path[0] = '.';
1359               w = exe_path + 1;
1360             }
1361           else
1362             {
1363               memcpy (exe_path, path, p - path);
1364               w = exe_path + (p - path);
1365             }
1366           if (!IS_DIRECTORY_SEP (w[-1]))
1367             {
1368               *w++ = '/';
1369             }
1370           strcpy (w, name);
1371
1372           /* ### #$%$#^$^@%$^#%@$ ! */
1373 #ifdef access
1374 #undef access
1375 #endif
1376
1377           if (!access (exe_path, X_OK))
1378             break;
1379           if (!*p)
1380             {
1381               /* Oh well, let's have some kind of default */
1382               sprintf (exe_path, "./%s", name);
1383               break;
1384             }
1385           path = p+1;
1386         }
1387     }
1388 #endif /* WIN32_NATIVE */
1389
1390   if (pdump_file_try (exe_path))
1391     {
1392       pdump_load_finish ();
1393       return 1;
1394     }
1395
1396 #ifdef WIN32_NATIVE
1397   if (pdump_resource_get ())
1398     {
1399       if (pdump_load_check ())
1400         {
1401           pdump_load_finish ();
1402           return 1;
1403         }
1404       pdump_free ();
1405     }
1406 #endif
1407
1408   return 0;
1409 }