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