comparison src/unexsunos4.c @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 74fd4e045ea6
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
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