(U+6215): Apply new conventions for glyph granularity.
[chise/xemacs-chise.git.1] / src / unexfreebsd.c
1 /* Code to do an unexec for FreeBSD-1.1 for a temacs linked -Bdynamic.
2    Derived from unexnetbsd.c, which was derived from unexsunos4.c
3    Copyright (C) 1992, 1993 Free Software Foundation, Inc.
4
5 This file is part of XEmacs.
6
7 XEmacs is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
10 later version.
11
12 XEmacs is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; see the file COPYING.  If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.  */
21
22 /* Synched up with: Not in FSF? */
23
24 /*
25 Created 29-Oct-92 by Harlan Sexton
26 Tweaked 06-Aug-93 by Dean Michaels to work with sun3.
27 Converted 01-Dec-93 by Paul Mackerras to work with NetBSD shared libraries.
28 Tweaked 26-Feb-94 by Shawn Carey for use with FreeBSD-1.1 shared libraries.
29 */
30
31 /********************** Included .h Files **************************/
32
33 #include <config.h>
34
35 #include <stdarg.h>
36 #include <sys/param.h>
37 #include <sys/mman.h>
38 #include <sys/file.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <string.h>
42 #include <stdio.h>
43 #include <errno.h>
44 #include <a.out.h>
45 #include <unistd.h>
46 #include <ctype.h>
47 #include <stab.h>
48 #include <sys/dir.h>
49 #include <link.h>
50
51 /********************** Macros *************************************/
52
53 #define SYS_ERR         strerror(errno)
54
55 #define MASK_UP(x,p_of_two) \
56  ((((unsigned long) (x)) + ((p_of_two) - 1)) & (~((p_of_two) - 1)))
57
58 #define MASK_DOWN(x,p_of_two) (((unsigned long) (x)) & (~((p_of_two) - 1)))
59
60 /********************** Typedefs and Structs ***********************/
61
62 struct translation_struct
63 {
64   long txtaddr;
65   long txtoff;
66   long dataddr;
67   long datoff;
68   long bssaddr;
69   long endaddr;
70 };
71
72 /********************** Function Prototypes/Declarations ***********/
73
74 static void unexec_error (const char *m, int use_errno, ...);
75 static int unexec_open (char *filename, int flag, int mode);
76 static caddr_t unexec_mmap (int fd, size_t len, int prot, int flags);
77 static long unexec_seek (int fd, long position);
78 static void unexec_read (int fd, long position, char *buf, int bytes);
79 static void unexec_write (int fd, long position, char *buf, int bytes);
80 static void unexec_pad (int fd, int bytes);
81 static void unexec_fstat (int fd, struct stat *statptr);
82 static void unexec_fchmod (int fd, int mode);
83 static long unexec_addr_to_offset (long addr, struct translation_struct *ts);
84 static void copy_relocation_site (struct relocation_info *ri, 
85                                   caddr_t from_base_addr, 
86                                   caddr_t to_base_addr, 
87                                   struct translation_struct *ts);
88 static void reset_symtab (struct nlist *start, struct nlist *end, 
89                           char *strtab, long edata_value, long end_value,
90                           int shlib_image);
91 static void reset_ldso_symtab (struct nzlist *start, struct nzlist *end, 
92                           char *strtab, long edata_value, long end_value,
93                           int shlib_image);
94 int run_time_remap (char *dummy);
95
96 /********************** Variables **********************************/
97
98 /* for reporting error messages from system calls */
99 extern int _DYNAMIC;
100 extern char **environ;             
101
102 static unsigned long sbrk_of_0_at_unexec;
103              
104 /*******************************************************************/
105
106 static void
107 unexec_error (const char *fmt, int use_errno, ...)
108 {
109   const char *err_msg = SYS_ERR;
110   va_list args;
111
112   fprintf (stderr, "unexec - ");
113   va_start (args, use_errno);
114   vfprintf (stderr, fmt, args);
115   va_end (args);
116
117   if (use_errno)
118     fprintf (stderr, ": %s", err_msg);
119   fprintf (stderr, "\n");
120   exit (1);
121   return;
122 }
123
124 static int
125 unexec_open (char *filename, int flag, int mode)
126 {
127   int fd;
128
129   errno = 0;
130
131   fd = open (filename, flag, mode);
132
133   if (fd < 0)
134     unexec_error ("Failure opening file %s", 1, filename);
135   return fd;
136 }
137
138 static caddr_t
139 unexec_mmap (int fd, size_t len, int prot, int flags)
140 {
141   caddr_t return_val;
142
143   unexec_seek (fd, 0);
144   errno = 0;
145   return_val = mmap (0, len, prot, flags, fd, 0);
146
147   if (return_val == (caddr_t) -1)
148     unexec_error ("Failure mmap'ing file", 1);
149   return return_val;
150 }
151
152
153 static long
154 unexec_seek (int fd, long position)
155 {
156   long seek_value;
157
158   if (fd <= 0)
159     unexec_error ("No file open in which to seek", 0);
160
161   errno = 0;
162
163   if (position < 0)
164     seek_value = (long) lseek (fd, 0, L_INCR);
165   else
166     seek_value = (long) lseek (fd, position, L_SET);
167
168   if (seek_value < 0)
169     unexec_error ("Failed to do a seek to 0x%x in %s", 1,
170                   position, "unexec() output file");
171
172   return seek_value;
173 }
174
175 static void
176 unexec_read (int fd, long position, char *buf, int bytes)
177 {
178   int n_read;
179   int remains = bytes;
180   position = unexec_seek (fd, position);
181
182   if (bytes < 0)
183     unexec_error ("Attempted read of %d bytes", 0, bytes);
184
185   errno = 0;
186
187   while (remains > 0)
188     {
189       n_read = read (fd, buf, remains);
190       if (n_read <= 0)
191         unexec_error ("Read failed for 0x%x bytes at offset 0x%x in %s",
192                       1, bytes, position, "unexec() output file");
193       buf += n_read;
194       remains -= n_read;
195     }
196
197   return;
198 }
199
200 static void
201 unexec_write (int fd, long position, char *buf, int bytes)
202 {
203   int n_written;
204   int remains = bytes;
205   position = unexec_seek (fd, position);
206
207   if (bytes < 0)
208     unexec_error ("Attempted write of %d bytes in %s",
209                   0, bytes, "unexec() output file");
210
211   errno = 0;
212
213   while (remains > 0)
214     {
215       n_written = write (fd, buf, remains);
216       if (n_written <= 0)
217         unexec_error ("Write failed for 0x%x bytes at offset 0x%x in %s",
218                       1, bytes, position, "unexec() output file");
219       buf += n_written;
220       remains -= n_written;
221     }
222
223   return;
224 }
225
226 static void 
227 unexec_pad (int fd, int bytes)
228 {
229   if (bytes > 0)
230     {
231       char buf[1024];
232       int remaining = bytes;
233
234       memset (buf, 0, sizeof (buf));
235
236       while (remaining > 0)
237         {
238           int this_write = (remaining > sizeof(buf))?sizeof(buf):remaining;
239           unexec_write (fd, -1, buf, this_write);
240           remaining -= this_write;
241         }
242     }
243 }
244
245 static void
246 unexec_fstat (int fd, struct stat *statptr)
247 {
248   errno = 0;
249   if (-1 == fstat (fd, statptr))
250     unexec_error ("fstat() failed for descriptor %d", 1, fd);
251   return;
252 }
253
254 static void
255 unexec_fchmod (int fd, int mode)
256 {
257   errno = 0;
258   if (-1 == fchmod (fd, mode))
259     unexec_error ("fchmod() failed for descriptor %d", 1, fd);
260   return;
261 }
262
263 static long
264 unexec_addr_to_offset (long addr, struct translation_struct *ts)
265                              
266 {
267   if ((addr < ts->txtaddr) || (addr >= ts->bssaddr))
268     return -1;
269   else if (addr >= ts->dataddr)
270     return ((long) ((addr - ts->dataddr) + ts->datoff));
271   else 
272     return ((long) ((addr - ts->txtaddr) + ts->txtoff));
273 }
274
275
276 /*
277  * "LD.SO" DATA AND SYMBOL TABLE OPERATIONS 
278  */
279
280 static void
281 copy_relocation_site (struct relocation_info *ri, 
282                       caddr_t from_base_addr,
283                       caddr_t to_base_addr,
284                       struct translation_struct *ts)
285 {
286   long offset;
287   caddr_t from, to;
288
289   /* We can get relocation sites in the bss region, for objects whose
290      contents are copied from a shared library.  We don't need or want
291      to restore these at present. */
292 #ifndef sparc
293   if (ri->r_copy)
294     return;
295 #else
296   /* Struct relocation_info_sparc doesn't have member r_copy.
297      Instead, we use the address to check if this is run-time-copied. */
298   if (ri->r_address >= ts->bssaddr && ri->r_address < ts->endaddr)
299     return;
300 #endif
301
302   offset = unexec_addr_to_offset (ri->r_address, ts);
303   if (offset == -1)
304     unexec_error ("bad relocation address 0x%x (0x%x)", 0, ri->r_address,
305                   ((long *)ri)[1]);
306
307   from = from_base_addr + offset;
308   to = to_base_addr + offset;
309   /* This stuff should be in a md_ file somewhere... */
310 #ifndef sparc
311   switch (ri->r_length)
312     {
313     case 0:
314       *((char *) to) = *((char *) from);
315       break;
316     case 1:
317       *((short *) to) = *((short *) from);
318       break;
319     case 2:
320       *((long *) to) = *((long *) from);
321       break;
322     default:
323       unexec_error ("unknown reloc length %d seen during unexec()",
324                     0, ri->r_length);
325       break;
326     }
327 #else /* sparc */
328   switch (ri->r_type)
329     {
330     case RELOC_8:
331     case RELOC_DISP8:
332       *((char *) to) = *((char *) from);
333       break;
334     case RELOC_16:
335     case RELOC_DISP16:
336       *((short *) to) = *((short *) from);
337       break;     
338     case RELOC_LO10:
339     case RELOC_13:     
340     case RELOC_22:     
341     case RELOC_HI22:
342     case RELOC_WDISP22:
343     case RELOC_WDISP30:
344     case RELOC_32:
345     case RELOC_DISP32:
346     case RELOC_GLOB_DAT:
347       *((long *) to) = *((long *) from);
348       break;
349     case RELOC_JMP_SLOT:
350       {
351         long *target = (long *) to;
352         long *source = (long *) from;
353         *target = *source;
354         target++;
355         source++;
356         *target = *source;
357         target++;
358         source++;
359         *target = *source;
360       }
361       break;
362     default:
363       unexec_error ("unknown reloc type %d seen during unexec()",
364                     0, ri->r_type);
365       break;
366     }
367 #endif /* sparc */
368 }
369
370 static void
371 reset_symtab (struct nlist *start, struct nlist *end, char *strtab,
372               long edata_value, long end_value, int shlib_image)
373 {
374   struct nlist *tmp = start;
375   int found_edata = 0;
376   int found_end = 0;
377      
378   while (tmp < end)
379     {
380       int type = tmp->n_type;
381
382       if ((type == (N_UNDF | N_EXT)) &&
383           (tmp->n_value != 0))
384         unexec_error ("unexec'ing image has COMMON symbols in it -- we quit!",
385                       0);
386      
387       if (!(type & N_STAB))
388         {
389           if (!found_edata &&
390               (type == (N_EXT | N_DATA)) &&
391               tmp->n_un.n_strx &&
392               !strcmp ("_edata", strtab + tmp->n_un.n_strx))
393             {
394               tmp->n_value = edata_value;
395               found_edata = 1;
396             }
397
398
399           if ((type & N_TYPE) == N_BSS)
400             {
401               if (!found_end &&
402                   (type == (N_EXT | N_BSS)) &&
403                   tmp->n_un.n_strx &&
404                   !strcmp ("_end", strtab + tmp->n_un.n_strx))
405                 {
406                   tmp->n_value = end_value;
407                   found_end = 1;
408                 }
409               else if (type & N_EXT)
410                 tmp->n_type = N_DATA | N_EXT;
411               else
412                 tmp->n_type = N_DATA;
413             }
414
415           /* the way things are being handled here, having sbrk() in the
416              image is fatal for an image linked with shared lib's (although 
417              the code could be modified to support it), but this should 
418              never happen anyway */
419           if (shlib_image &&
420               (type == (N_EXT | N_TEXT)) &&
421               tmp->n_un.n_strx &&
422               !strcmp ("_sbrk", strtab + tmp->n_un.n_strx))
423             unexec_error ("unexec'd shlib image has sbrk() in it -- we quit!",
424                           0);
425         }
426
427       tmp++;
428     }
429
430      
431 static void
432 reset_ldso_symtab (struct nzlist *start, struct nzlist *end, char *strtab,
433               long edata_value, long end_value, int shlib_image)
434 {
435   struct nzlist *tmp = start;
436   int found_edata = 0;
437   int found_end = 0;
438      
439   while (tmp < end) {
440     int type = tmp->nz_type;
441 /*
442  * the following code breaks under FreeBSD-1.1-BETA, but everything
443  * seems to work perfectly if it's commented out.  This did not break
444  * anything until the changes to ld.so were made.
445  */
446 /*
447     if ((type == (N_UNDF | N_EXT)) && (tmp->nz_value != 0))
448       unexec_error("unexec'ing image has COMMON symbols in rel -- we quit!",0);
449 */
450     if (!(type & N_STAB)) {
451       if (!found_edata &&
452           (type == (N_EXT | N_DATA)) &&
453           !strcmp ("_edata", strtab + tmp->nz_strx)) {
454         tmp->nz_value = edata_value;
455         found_edata = 1;
456       }
457
458       if ((type & N_TYPE) == N_BSS) {
459         if (!found_end &&
460             (type == (N_EXT | N_BSS)) &&
461             !strcmp ("_end", strtab + tmp->nz_strx)) {
462           tmp->nz_value = end_value;
463           found_end = 1;
464         } else if (type & N_EXT)
465           tmp->nz_type = N_DATA | N_EXT;
466         else
467           tmp->nz_type = N_DATA;
468       }
469
470       /* the way things are being handled here, having sbrk() in the
471          image is fatal for an image linked with shared lib's (although 
472          the code could be modified to support it), but this should 
473          never happen anyway */
474       if (shlib_image &&
475           (type == (N_EXT | N_TEXT)) &&
476           !strcmp ("_sbrk", strtab + tmp->nz_strx))
477         unexec_error("unexec'd shlib image has sbrk() ref -- we quit!", 0);
478     }
479     tmp++;
480   }
481
482      
483 extern int getpagesize (void);
484
485 /*
486  * EXPORTED FUNCTIONS 
487  */
488
489 /* this has to be a global variable to prevent the optimizers from
490  * assuming that it can not be 0.  
491 */
492 static void *dynamic_addr = (void *) &_DYNAMIC;
493
494 int
495 unexec (char *new_name, char *old_name,
496         unsigned int emacs_edata, unsigned int dummy1, unsigned int dummy2)
497 {
498   /* ld.so data */
499   struct _dynamic *ld = 0;
500   struct section_dispatch_table *ld2 = 0;
501   /* old and new state */
502   int old_fd;
503   int new_fd;
504   caddr_t old_base_addr;
505   caddr_t new_base_addr;
506   struct exec old_hdr;
507   struct exec new_hdr;
508   struct stat old_buf;
509   struct stat new_buf;
510   /* some process specific "constants" */
511   unsigned long n_pagsiz, new_edata;
512   long page_size = getpagesize ();
513   caddr_t plt_end;
514   caddr_t current_break = (caddr_t) sbrk (0);
515
516   if (!page_size)
517     unexec_error ("unexec() failed because we can't get the size of a page!",
518                   0);
519
520   /* see if this is a -Bdynamic image -- if so, find ld.so structures */
521   if (dynamic_addr)
522     {
523       ld = (struct _dynamic *) dynamic_addr;
524       ld2 = ld->d_un.d_sdt;
525       if (ld->d_version < LD_VERSION_BSD)
526         unexec_error ("%s linked with obsolete version of ld -- we quit!",
527                       0, old_name);
528     }
529
530   /* open the old and new files, figuring out how big the old one is
531      so that we can map it in */
532   old_fd = unexec_open (old_name, O_RDONLY, 0);
533   new_fd = unexec_open (new_name, O_RDWR | O_CREAT | O_TRUNC, 0666);
534
535   /* setup the header and the statbuf for old_fd */
536   unexec_read (old_fd, 0, (char *) &old_hdr, sizeof (old_hdr));
537   unexec_fstat (old_fd, &old_buf);
538
539
540   /* set up some important constants */
541   n_pagsiz = __LDPGSZ;
542   if (dynamic_addr)
543     plt_end = (caddr_t) MASK_UP (ld2->sdt_plt + ld2->sdt_plt_sz, sizeof (double));
544   else
545     plt_end = (caddr_t) N_DATADDR (old_hdr);
546
547 #if 0
548   /* never write protect the variable "environ", defined in /lib/crt0.o, and
549      set in process.c and callproc.c */
550   mprotect_bottom_addr = ((unsigned long) &environ) + sizeof (char **);
551   /* never protect ABOVE the end of data emacs_edata specified */
552   mprotect_top_addr = MIN (emacs_edata, N_DATADDR (old_hdr) + old_hdr.a_data);
553 #endif
554
555   /* Set up the image of the old file */
556   old_base_addr = unexec_mmap (old_fd, old_buf.st_size, PROT_READ,
557                                MAP_FILE | MAP_PRIVATE);
558   close (old_fd);
559
560   /* set up the new exec */
561   new_hdr = old_hdr;
562   new_edata = (unsigned long) MASK_UP (current_break, n_pagsiz);
563   new_hdr.a_data = new_edata - ((unsigned long) N_DATADDR (old_hdr));
564   new_hdr.a_bss  = 0;
565
566   /* set up this variable, in case we want to reset "the break" 
567      when restarting */
568   sbrk_of_0_at_unexec = ((unsigned long) MASK_UP (current_break, n_pagsiz));
569      
570   /* Write out the first approximation to the new file. The sizes of
571      each section will be correct, but there will be a number of 
572      corrections that will need to be made. */
573   {
574     long old_datoff = N_DATOFF (old_hdr);
575     long old_dataddr = N_DATADDR (old_hdr);
576     long new_treloff = N_RELOFF (new_hdr);
577     long old_treloff = N_RELOFF (old_hdr);
578     long ld_so_size = ((unsigned long) plt_end) - old_dataddr;
579     long real_data_size = current_break - plt_end;
580     long pad_size = 
581       MASK_UP (current_break, n_pagsiz) - ((unsigned long) current_break);
582
583
584     /* First, write the text segment with new header -- copy everything until
585        the start of the data segment from the old file, and then go back and 
586        write the new header. */
587     unexec_write (new_fd, 0, old_base_addr, old_datoff + ld_so_size);
588     unexec_write (new_fd, 0, (char *) &new_hdr, sizeof (new_hdr));
589
590     /* Copy the rest of the data segment from the running image. */
591     unexec_write (new_fd, old_datoff + ld_so_size, 
592                   plt_end, real_data_size);
593
594     /* pad out the data segment */
595     unexec_pad (new_fd, pad_size);
596     
597     /* Finally, copy the symbol table information from the old file. */
598     unexec_write (new_fd, new_treloff,
599                   old_base_addr + old_treloff,
600                   old_buf.st_size - old_treloff);
601   }
602      
603      
604   /* Next, map in the output file so that we can jump around fixing it
605      up. We retain the old file so that we can refer to it. */
606   unexec_fstat (new_fd, &new_buf);
607   new_base_addr = unexec_mmap (new_fd, 
608                                MASK_UP (new_buf.st_size, page_size),
609                                PROT_READ | PROT_WRITE,
610                                MAP_FILE | MAP_SHARED);
611
612
613
614   /* We need to do 2 things. First, make sure that _edata and _end (and
615      hence, curbrk) are set to the correct values. At the same time, for
616      neatness and to help with debugging, mark all the types of all ld.so
617      and nm BSS symbols in the new file to be DATA, and make sure that
618      there are no COMMON symbols in the output file, as any references to
619      these can lose really big. Second, reset all of the ld.so "relocation
620      sites" in the new file to have the values that appear in the old file
621      -- the failure to do this was the biggest loser in the old version of
622      this code. */
623
624   /* STEP 1 */
625   /* Reset the regular symbol table first. */
626   reset_symtab ((struct nlist *) (new_base_addr + N_SYMOFF(new_hdr)),
627                 (struct nlist *) (new_base_addr + N_SYMOFF(new_hdr) +
628                                   new_hdr.a_syms),
629                 (char *) (new_base_addr + N_STROFF(new_hdr)),
630                 new_edata, new_edata,
631                 !!dynamic_addr);
632
633   /* Now reset the ld.so symbol table. */
634   if (dynamic_addr)
635     {
636       struct translation_struct ts;
637       struct relocation_info *tmp, *end;
638       caddr_t syms, strings;
639
640       /* set up the structure that we use to translate addresses in the
641          old file into file offsets */
642       ts.txtaddr = N_TXTADDR (old_hdr);
643       ts.txtoff = N_TXTOFF (old_hdr);
644       ts.dataddr = N_DATADDR (old_hdr);
645       ts.datoff = N_DATOFF (old_hdr);
646       ts.bssaddr = N_DATADDR (old_hdr) + old_hdr.a_data;
647       ts.endaddr = ts.bssaddr + old_hdr.a_bss;
648
649       syms = new_base_addr + unexec_addr_to_offset(ld2->sdt_nzlist, &ts);
650       strings = new_base_addr + unexec_addr_to_offset(ld2->sdt_strings, &ts);
651       reset_ldso_symtab ((struct nzlist *) syms, (struct nzlist *) strings,
652                          (char *) strings,
653                          new_edata, new_edata,
654                          !!dynamic_addr);
655
656       /* STEP 2 */
657       tmp = (struct relocation_info *)
658         (old_base_addr + unexec_addr_to_offset(ld2->sdt_rel, &ts));
659       end = (struct relocation_info *)
660         (old_base_addr + unexec_addr_to_offset(ld2->sdt_hash, &ts));
661       while (tmp < end)
662         {
663           copy_relocation_site (tmp, old_base_addr, new_base_addr, &ts);
664           tmp++;
665         }
666     }
667      
668   /* get rid of the mmap-ed file space and make the output file 
669      executable -- then quit */
670   munmap (new_base_addr, MASK_UP (new_buf.st_size, page_size));
671   munmap (old_base_addr, MASK_UP (old_buf.st_size, page_size));
672   unexec_fchmod (new_fd, 0755);
673   close (new_fd);
674   return 0;
675 }
676
677
678 int
679 run_time_remap (char *dummy)
680 {
681       unsigned long current_sbrk = (unsigned long) sbrk (0);
682
683 #if __FreeBSD_version < 300000 /* 2.x can work with this code */
684       if (sbrk_of_0_at_unexec < current_sbrk)
685         {
686           if (sbrk_of_0_at_unexec != 0)
687             fprintf (stderr, "Absurd new brk addr = %lx (current = %lx)\n", 
688                      sbrk_of_0_at_unexec, current_sbrk);
689         }
690       else
691 #endif
692         if (sbrk_of_0_at_unexec > current_sbrk)
693         {
694           errno = 0;
695           if (brk ((caddr_t) sbrk_of_0_at_unexec))
696             fprintf (stderr, "failed to change brk addr to %lx: %s\n", 
697                      sbrk_of_0_at_unexec, SYS_ERR);
698         }
699
700 #if 0
701 /* with proper COW, i don't think we really need to do this... */
702   {
703     long page_size = getpagesize();
704     unsigned long base_addr = MASK_UP (mprotect_bottom_addr, page_size);
705     unsigned long top_addr = MASK_DOWN (mprotect_top_addr, page_size);
706     long len = top_addr - base_addr;
707
708     if (len > 0)
709       {
710         errno = 0;
711         if (mprotect ((caddr_t) base_addr, len, PROT_READ | PROT_EXEC))
712           fprintf (stderr, "failed to change protection on data pages: %s\n",
713                    SYS_ERR);
714       }
715   }
716 #endif
717              
718   return 0;
719 }