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

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children ac2d302a0011
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
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 <a.out.h>
44 #include <unistd.h>
45 #include <ctype.h>
46 #include <stab.h>
47 #include <sys/dir.h>
48 #include <link.h>
49
50 /********************** Macros *************************************/
51
52 #define SYS_ERR strerror(errno)
53
54 #define MASK_UP(x,p_of_two) \
55 ((((unsigned long) (x)) + ((p_of_two) - 1)) & (~((p_of_two) - 1)))
56
57 #define MASK_DOWN(x,p_of_two) (((unsigned long) (x)) & (~((p_of_two) - 1)))
58
59 /********************** Typedefs and Structs ***********************/
60
61 struct translation_struct
62 {
63 long txtaddr;
64 long txtoff;
65 long dataddr;
66 long datoff;
67 long bssaddr;
68 long endaddr;
69 };
70
71 /********************** Function Prototypes/Declarations ***********/
72
73 static void unexec_error (const char *m, int use_errno, ...);
74 static int unexec_open (char *filename, int flag, int mode);
75 static caddr_t unexec_mmap (int fd, size_t len, int prot, int flags);
76 static long unexec_seek (int fd, long position);
77 static void unexec_read (int fd, long position, char *buf, int bytes);
78 static void unexec_write (int fd, long position, char *buf, int bytes);
79 static void unexec_pad (int fd, int bytes);
80 static void unexec_fstat (int fd, struct stat *statptr);
81 static void unexec_fchmod (int fd, int mode);
82 static long unexec_addr_to_offset (long addr, struct translation_struct *ts);
83 static void copy_relocation_site (struct relocation_info *ri,
84 caddr_t from_base_addr,
85 caddr_t to_base_addr,
86 struct translation_struct *ts);
87 static void reset_symtab (struct nlist *start, struct nlist *end,
88 char *strtab, long edata_value, long end_value,
89 int shlib_image);
90 static void reset_ldso_symtab (struct nzlist *start, struct nzlist *end,
91 char *strtab, long edata_value, long end_value,
92 int shlib_image);
93 int run_time_remap (char *dummy);
94
95 /********************** Variables **********************************/
96
97 /* for reporting error messages from system calls */
98 extern int errno;
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 if (ri->r_copy)
293 return;
294
295 offset = unexec_addr_to_offset (ri->r_address, ts);
296 if (offset == -1)
297 unexec_error ("bad relocation address 0x%x (0x%x)", 0, ri->r_address,
298 ((long *)ri)[1]);
299
300 from = from_base_addr + offset;
301 to = to_base_addr + offset;
302 /* This stuff should be in a md_ file somewhere... */
303 #ifndef sparc
304 switch (ri->r_length)
305 {
306 case 0:
307 *((char *) to) = *((char *) from);
308 break;
309 case 1:
310 *((short *) to) = *((short *) from);
311 break;
312 case 2:
313 *((long *) to) = *((long *) from);
314 break;
315 default:
316 unexec_error ("unknown reloc length %d seen during unexec()",
317 0, ri->r_length);
318 break;
319 }
320 #else /* sparc */
321 switch (ri->r_type)
322 {
323 case RELOC_8:
324 case RELOC_DISP8:
325 *((char *) to) = *((char *) from);
326 break;
327 case RELOC_16:
328 case RELOC_DISP16:
329 *((short *) to) = *((short *) from);
330 break;
331 case RELOC_LO10:
332 case RELOC_13:
333 case RELOC_22:
334 case RELOC_HI22:
335 case RELOC_WDISP22:
336 case RELOC_WDISP30:
337 case RELOC_32:
338 case RELOC_DISP32:
339 case RELOC_GLOB_DAT:
340 *((long *) to) = *((long *) from);
341 break;
342 case RELOC_JMP_SLOT:
343 {
344 long *target = (long *) to;
345 long *source = (long *) from;
346 *target = *source;
347 target++;
348 source++;
349 *target = *source;
350 target++;
351 source++;
352 *target = *source;
353 }
354 default:
355 unexec_error ("unknown reloc type %d seen during unexec()",
356 0, ri->r_type);
357 break;
358 }
359 #endif /* sparc */
360 }
361
362 static void
363 reset_symtab (struct nlist *start, struct nlist *end, char *strtab,
364 long edata_value, long end_value, int shlib_image)
365 {
366 struct nlist *tmp = start;
367 int found_edata = 0;
368 int found_end = 0;
369
370 while (tmp < end)
371 {
372 int type = tmp->n_type;
373
374 if ((type == (N_UNDF | N_EXT)) &&
375 (tmp->n_value != 0))
376 unexec_error ("unexec'ing image has COMMON symbols in it -- we quit!",
377 0);
378
379 if (!(type & N_STAB))
380 {
381 if (!found_edata &&
382 (type == (N_EXT | N_DATA)) &&
383 tmp->n_un.n_strx &&
384 !strcmp ("_edata", strtab + tmp->n_un.n_strx))
385 {
386 tmp->n_value = edata_value;
387 found_edata = 1;
388 }
389
390
391 if ((type & N_TYPE) == N_BSS)
392 {
393 if (!found_end &&
394 (type == (N_EXT | N_BSS)) &&
395 tmp->n_un.n_strx &&
396 !strcmp ("_end", strtab + tmp->n_un.n_strx))
397 {
398 tmp->n_value = end_value;
399 found_end = 1;
400 }
401 else if (type & N_EXT)
402 tmp->n_type = N_DATA | N_EXT;
403 else
404 tmp->n_type = N_DATA;
405 }
406
407 /* the way things are being handled here, having sbrk() in the
408 image is fatal for an image linked with shared lib's (although
409 the code could be modified to support it), but this should
410 never happen anyway */
411 if (shlib_image &&
412 (type == (N_EXT | N_TEXT)) &&
413 tmp->n_un.n_strx &&
414 !strcmp ("_sbrk", strtab + tmp->n_un.n_strx))
415 unexec_error ("unexec'd shlib image has sbrk() in it -- we quit!",
416 0);
417 }
418
419 tmp++;
420 }
421 }
422
423 static void
424 reset_ldso_symtab (struct nzlist *start, struct nzlist *end, char *strtab,
425 long edata_value, long end_value, int shlib_image)
426 {
427 struct nzlist *tmp = start;
428 int found_edata = 0;
429 int found_end = 0;
430
431 while (tmp < end) {
432 int type = tmp->nz_type;
433 /*
434 * the following code breaks under FreeBSD-1.1-BETA, but everything
435 * seems to work perfectly if it's commented out. This did not break
436 * anything until the changes to ld.so were made.
437 */
438 /*
439 if ((type == (N_UNDF | N_EXT)) && (tmp->nz_value != 0))
440 unexec_error("unexec'ing image has COMMON symbols in rel -- we quit!",0);
441 */
442 if (!(type & N_STAB)) {
443 if (!found_edata &&
444 (type == (N_EXT | N_DATA)) &&
445 !strcmp ("_edata", strtab + tmp->nz_strx)) {
446 tmp->nz_value = edata_value;
447 found_edata = 1;
448 }
449
450 if ((type & N_TYPE) == N_BSS) {
451 if (!found_end &&
452 (type == (N_EXT | N_BSS)) &&
453 !strcmp ("_end", strtab + tmp->nz_strx)) {
454 tmp->nz_value = end_value;
455 found_end = 1;
456 } else if (type & N_EXT)
457 tmp->nz_type = N_DATA | N_EXT;
458 else
459 tmp->nz_type = N_DATA;
460 }
461
462 /* the way things are being handled here, having sbrk() in the
463 image is fatal for an image linked with shared lib's (although
464 the code could be modified to support it), but this should
465 never happen anyway */
466 if (shlib_image &&
467 (type == (N_EXT | N_TEXT)) &&
468 !strcmp ("_sbrk", strtab + tmp->nz_strx))
469 unexec_error("unexec'd shlib image has sbrk() ref -- we quit!", 0);
470 }
471 tmp++;
472 }
473 }
474
475 extern int getpagesize (void);
476
477 /*
478 * EXPORTED FUNCTIONS
479 */
480
481 /* this has to be a global variable to prevent the optimizers from
482 * assuming that it can not be 0.
483 */
484 static void *dynamic_addr = (void *) &_DYNAMIC;
485
486 int
487 unexec (char *new_name, char *old_name,
488 unsigned int emacs_edata, unsigned int dummy1, unsigned int dummy2)
489 {
490 /* ld.so data */
491 struct _dynamic *ld = 0;
492 struct section_dispatch_table *ld2 = 0;
493 /* old and new state */
494 int old_fd;
495 int new_fd;
496 caddr_t old_base_addr;
497 caddr_t new_base_addr;
498 struct exec old_hdr;
499 struct exec new_hdr;
500 struct stat old_buf;
501 struct stat new_buf;
502 /* some process specific "constants" */
503 unsigned long n_pagsiz, new_edata;
504 long page_size = getpagesize ();
505 caddr_t plt_end;
506 caddr_t current_break = (caddr_t) sbrk (0);
507
508 if (!page_size)
509 unexec_error ("unexec() failed because we can't get the size of a page!",
510 0);
511
512 /* see if this is a -Bdynamic image -- if so, find ld.so structures */
513 if (dynamic_addr)
514 {
515 ld = (struct _dynamic *) dynamic_addr;
516 ld2 = ld->d_un.d_sdt;
517 if (ld->d_version < LD_VERSION_BSD)
518 unexec_error ("%s linked with obsolete version of ld -- we quit!",
519 0, old_name);
520 }
521
522 /* open the old and new files, figuring out how big the old one is
523 so that we can map it in */
524 old_fd = unexec_open (old_name, O_RDONLY, 0);
525 new_fd = unexec_open (new_name, O_RDWR | O_CREAT | O_TRUNC, 0666);
526
527 /* setup the header and the statbuf for old_fd */
528 unexec_read (old_fd, 0, (char *) &old_hdr, sizeof (old_hdr));
529 unexec_fstat (old_fd, &old_buf);
530
531
532 /* set up some important constants */
533 n_pagsiz = __LDPGSZ;
534 if (dynamic_addr)
535 plt_end = (caddr_t) MASK_UP (ld2->sdt_plt + ld2->sdt_plt_sz, sizeof (double));
536 else
537 plt_end = (caddr_t) N_DATADDR (old_hdr);
538
539 #if 0
540 /* never write protect the variable "environ", defined in /lib/crt0.o, and
541 set in process.c and callproc.c */
542 mprotect_bottom_addr = ((unsigned long) &environ) + sizeof (char **);
543 /* never protect ABOVE the end of data emacs_edata specified */
544 mprotect_top_addr = MIN (emacs_edata, N_DATADDR (old_hdr) + old_hdr.a_data);
545 #endif
546
547 /* Set up the image of the old file */
548 old_base_addr = unexec_mmap (old_fd, old_buf.st_size, PROT_READ,
549 MAP_FILE | MAP_PRIVATE);
550 close (old_fd);
551
552 /* set up the new exec */
553 new_hdr = old_hdr;
554 new_edata = (unsigned long) MASK_UP (current_break, n_pagsiz);
555 new_hdr.a_data = new_edata - ((unsigned long) N_DATADDR (old_hdr));
556 new_hdr.a_bss = 0;
557
558 /* set up this variable, in case we want to reset "the break"
559 when restarting */
560 sbrk_of_0_at_unexec = ((unsigned long) MASK_UP (current_break, n_pagsiz));
561
562 /* Write out the first approximation to the new file. The sizes of
563 each section will be correct, but there will be a number of
564 corrections that will need to be made. */
565 {
566 long old_datoff = N_DATOFF (old_hdr);
567 long old_dataddr = N_DATADDR (old_hdr);
568 long new_treloff = N_RELOFF (new_hdr);
569 long old_treloff = N_RELOFF (old_hdr);
570 long ld_so_size = ((unsigned long) plt_end) - old_dataddr;
571 long real_data_size = current_break - plt_end;
572 long pad_size =
573 MASK_UP (current_break, n_pagsiz) - ((unsigned long) current_break);
574
575
576 /* First, write the text segment with new header -- copy everything until
577 the start of the data segment from the old file, and then go back and
578 write the new header. */
579 unexec_write (new_fd, 0, old_base_addr, old_datoff + ld_so_size);
580 unexec_write (new_fd, 0, (char *) &new_hdr, sizeof (new_hdr));
581
582 /* Copy the rest of the data segment from the running image. */
583 unexec_write (new_fd, old_datoff + ld_so_size,
584 plt_end, real_data_size);
585
586 /* pad out the data segment */
587 unexec_pad (new_fd, pad_size);
588
589 /* Finally, copy the symbol table information from the old file. */
590 unexec_write (new_fd, new_treloff,
591 old_base_addr + old_treloff,
592 old_buf.st_size - old_treloff);
593 }
594
595
596 /* Next, map in the output file so that we can jump around fixing it
597 up. We retain the old file so that we can refer to it. */
598 unexec_fstat (new_fd, &new_buf);
599 new_base_addr = unexec_mmap (new_fd,
600 MASK_UP (new_buf.st_size, page_size),
601 PROT_READ | PROT_WRITE,
602 MAP_FILE | MAP_SHARED);
603
604
605
606 /* We need to do 2 things. First, make sure that _edata and _end (and
607 hence, curbrk) are set to the correct values. At the same time, for
608 neatness and to help with debugging, mark all the types of all ld.so
609 and nm BSS symbols in the new file to be DATA, and make sure that
610 there are no COMMON symbols in the output file, as any references to
611 these can lose really big. Second, reset all of the ld.so "relocation
612 sites" in the new file to have the values that appear in the old file
613 -- the failure to do this was the biggest loser in the old version of
614 this code. */
615
616 /* STEP 1 */
617 /* Reset the regular symbol table first. */
618 reset_symtab ((struct nlist *) (new_base_addr + N_SYMOFF(new_hdr)),
619 (struct nlist *) (new_base_addr + N_SYMOFF(new_hdr) +
620 new_hdr.a_syms),
621 (char *) (new_base_addr + N_STROFF(new_hdr)),
622 new_edata, new_edata,
623 !!dynamic_addr);
624
625 /* Now reset the ld.so symbol table. */
626 if (dynamic_addr)
627 {
628 struct translation_struct ts;
629 struct relocation_info *tmp, *end;
630 caddr_t syms, strings;
631
632 /* set up the structure that we use to translate addresses in the
633 old file into file offsets */
634 ts.txtaddr = N_TXTADDR (old_hdr);
635 ts.txtoff = N_TXTOFF (old_hdr);
636 ts.dataddr = N_DATADDR (old_hdr);
637 ts.datoff = N_DATOFF (old_hdr);
638 ts.bssaddr = N_DATADDR (old_hdr) + old_hdr.a_data;
639 ts.endaddr = ts.bssaddr + old_hdr.a_bss;
640
641 syms = new_base_addr + unexec_addr_to_offset(ld2->sdt_nzlist, &ts);
642 strings = new_base_addr + unexec_addr_to_offset(ld2->sdt_strings, &ts);
643 reset_ldso_symtab ((struct nzlist *) syms, (struct nzlist *) strings,
644 (char *) strings,
645 new_edata, new_edata,
646 !!dynamic_addr);
647
648 /* STEP 2 */
649 tmp = (struct relocation_info *)
650 (old_base_addr + unexec_addr_to_offset(ld2->sdt_rel, &ts));
651 end = (struct relocation_info *)
652 (old_base_addr + unexec_addr_to_offset(ld2->sdt_hash, &ts));
653 while (tmp < end)
654 {
655 copy_relocation_site (tmp, old_base_addr, new_base_addr, &ts);
656 tmp++;
657 }
658 }
659
660 /* get rid of the mmap-ed file space and make the output file
661 executable -- then quit */
662 munmap (new_base_addr, MASK_UP (new_buf.st_size, page_size));
663 munmap (old_base_addr, MASK_UP (old_buf.st_size, page_size));
664 unexec_fchmod (new_fd, 0755);
665 close (new_fd);
666 return 0;
667 }
668
669
670 int
671 run_time_remap (char *dummy)
672 {
673 unsigned long current_sbrk = (unsigned long) sbrk (0);
674
675 if (sbrk_of_0_at_unexec < current_sbrk)
676 {
677 if (sbrk_of_0_at_unexec != 0)
678 fprintf (stderr, "Absurd new brk addr = 0x%x (current = 0x%x)\n",
679 sbrk_of_0_at_unexec, current_sbrk);
680 }
681 else
682 {
683 errno = 0;
684 if (brk ((caddr_t) sbrk_of_0_at_unexec))
685 fprintf (stderr, "failed to change brk addr to 0x%x: %s\n",
686 sbrk_of_0_at_unexec, SYS_ERR);
687 }
688
689 #if 0
690 /* with proper COW, i don't think we really need to do this... */
691 {
692 long page_size = getpagesize();
693 unsigned long base_addr = MASK_UP (mprotect_bottom_addr, page_size);
694 unsigned long top_addr = MASK_DOWN (mprotect_top_addr, page_size);
695 long len = top_addr - base_addr;
696
697 if (len > 0)
698 {
699 errno = 0;
700 if (mprotect ((caddr_t) base_addr, len, PROT_READ | PROT_EXEC))
701 fprintf (stderr, "failed to change protection on data pages: %s\n",
702 SYS_ERR);
703 }
704 }
705 #endif
706
707 return 0;
708 }