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.
5 This file is part of XEmacs.
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
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
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. */
22 /* Synched up with: Not in FSF? */
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.
31 /********************** Included .h Files **************************/
36 #include <sys/param.h>
40 #include <sys/types.h>
51 /********************** Macros *************************************/
53 #define SYS_ERR strerror(errno)
55 #define MASK_UP(x,p_of_two) \
56 ((((unsigned long) (x)) + ((p_of_two) - 1)) & (~((p_of_two) - 1)))
58 #define MASK_DOWN(x,p_of_two) (((unsigned long) (x)) & (~((p_of_two) - 1)))
60 /********************** Typedefs and Structs ***********************/
62 struct translation_struct
72 /********************** Function Prototypes/Declarations ***********/
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,
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,
91 static void reset_ldso_symtab (struct nzlist *start, struct nzlist *end,
92 char *strtab, long edata_value, long end_value,
94 int run_time_remap (char *dummy);
96 /********************** Variables **********************************/
98 /* for reporting error messages from system calls */
100 extern char **environ;
102 static unsigned long sbrk_of_0_at_unexec;
104 /*******************************************************************/
107 unexec_error (const char *fmt, int use_errno, ...)
109 const char *err_msg = SYS_ERR;
112 fprintf (stderr, "unexec - ");
113 va_start (args, use_errno);
114 vfprintf (stderr, fmt, args);
118 fprintf (stderr, ": %s", err_msg);
119 fprintf (stderr, "\n");
125 unexec_open (char *filename, int flag, int mode)
131 fd = open (filename, flag, mode);
134 unexec_error ("Failure opening file %s", 1, filename);
139 unexec_mmap (int fd, size_t len, int prot, int flags)
145 return_val = mmap (0, len, prot, flags, fd, 0);
147 if (return_val == (caddr_t) -1)
148 unexec_error ("Failure mmap'ing file", 1);
154 unexec_seek (int fd, long position)
159 unexec_error ("No file open in which to seek", 0);
164 seek_value = (long) lseek (fd, 0, L_INCR);
166 seek_value = (long) lseek (fd, position, L_SET);
169 unexec_error ("Failed to do a seek to 0x%x in %s", 1,
170 position, "unexec() output file");
176 unexec_read (int fd, long position, char *buf, int bytes)
180 position = unexec_seek (fd, position);
183 unexec_error ("Attempted read of %d bytes", 0, bytes);
189 n_read = read (fd, buf, remains);
191 unexec_error ("Read failed for 0x%x bytes at offset 0x%x in %s",
192 1, bytes, position, "unexec() output file");
201 unexec_write (int fd, long position, char *buf, int bytes)
205 position = unexec_seek (fd, position);
208 unexec_error ("Attempted write of %d bytes in %s",
209 0, bytes, "unexec() output file");
215 n_written = write (fd, buf, remains);
217 unexec_error ("Write failed for 0x%x bytes at offset 0x%x in %s",
218 1, bytes, position, "unexec() output file");
220 remains -= n_written;
227 unexec_pad (int fd, int bytes)
232 int remaining = bytes;
234 memset (buf, 0, sizeof (buf));
236 while (remaining > 0)
238 int this_write = (remaining > sizeof(buf))?sizeof(buf):remaining;
239 unexec_write (fd, -1, buf, this_write);
240 remaining -= this_write;
246 unexec_fstat (int fd, struct stat *statptr)
249 if (-1 == fstat (fd, statptr))
250 unexec_error ("fstat() failed for descriptor %d", 1, fd);
255 unexec_fchmod (int fd, int mode)
258 if (-1 == fchmod (fd, mode))
259 unexec_error ("fchmod() failed for descriptor %d", 1, fd);
264 unexec_addr_to_offset (long addr, struct translation_struct *ts)
267 if ((addr < ts->txtaddr) || (addr >= ts->bssaddr))
269 else if (addr >= ts->dataddr)
270 return ((long) ((addr - ts->dataddr) + ts->datoff));
272 return ((long) ((addr - ts->txtaddr) + ts->txtoff));
277 * "LD.SO" DATA AND SYMBOL TABLE OPERATIONS
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)
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. */
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)
302 offset = unexec_addr_to_offset (ri->r_address, ts);
304 unexec_error ("bad relocation address 0x%x (0x%x)", 0, ri->r_address,
307 from = from_base_addr + offset;
308 to = to_base_addr + offset;
309 /* This stuff should be in a md_ file somewhere... */
311 switch (ri->r_length)
314 *((char *) to) = *((char *) from);
317 *((short *) to) = *((short *) from);
320 *((long *) to) = *((long *) from);
323 unexec_error ("unknown reloc length %d seen during unexec()",
332 *((char *) to) = *((char *) from);
336 *((short *) to) = *((short *) from);
347 *((long *) to) = *((long *) from);
351 long *target = (long *) to;
352 long *source = (long *) from;
363 unexec_error ("unknown reloc type %d seen during unexec()",
371 reset_symtab (struct nlist *start, struct nlist *end, char *strtab,
372 long edata_value, long end_value, int shlib_image)
374 struct nlist *tmp = start;
380 int type = tmp->n_type;
382 if ((type == (N_UNDF | N_EXT)) &&
384 unexec_error ("unexec'ing image has COMMON symbols in it -- we quit!",
387 if (!(type & N_STAB))
390 (type == (N_EXT | N_DATA)) &&
392 !strcmp ("_edata", strtab + tmp->n_un.n_strx))
394 tmp->n_value = edata_value;
399 if ((type & N_TYPE) == N_BSS)
402 (type == (N_EXT | N_BSS)) &&
404 !strcmp ("_end", strtab + tmp->n_un.n_strx))
406 tmp->n_value = end_value;
409 else if (type & N_EXT)
410 tmp->n_type = N_DATA | N_EXT;
412 tmp->n_type = N_DATA;
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 */
420 (type == (N_EXT | N_TEXT)) &&
422 !strcmp ("_sbrk", strtab + tmp->n_un.n_strx))
423 unexec_error ("unexec'd shlib image has sbrk() in it -- we quit!",
432 reset_ldso_symtab (struct nzlist *start, struct nzlist *end, char *strtab,
433 long edata_value, long end_value, int shlib_image)
435 struct nzlist *tmp = start;
440 int type = tmp->nz_type;
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.
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);
450 if (!(type & N_STAB)) {
452 (type == (N_EXT | N_DATA)) &&
453 !strcmp ("_edata", strtab + tmp->nz_strx)) {
454 tmp->nz_value = edata_value;
458 if ((type & N_TYPE) == N_BSS) {
460 (type == (N_EXT | N_BSS)) &&
461 !strcmp ("_end", strtab + tmp->nz_strx)) {
462 tmp->nz_value = end_value;
464 } else if (type & N_EXT)
465 tmp->nz_type = N_DATA | N_EXT;
467 tmp->nz_type = N_DATA;
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 */
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);
483 extern int getpagesize (void);
489 /* this has to be a global variable to prevent the optimizers from
490 * assuming that it can not be 0.
492 static void *dynamic_addr = (void *) &_DYNAMIC;
495 unexec (char *new_name, char *old_name,
496 unsigned int emacs_edata, unsigned int dummy1, unsigned int dummy2)
499 struct _dynamic *ld = 0;
500 struct section_dispatch_table *ld2 = 0;
501 /* old and new state */
504 caddr_t old_base_addr;
505 caddr_t new_base_addr;
510 /* some process specific "constants" */
511 unsigned long n_pagsiz, new_edata;
512 long page_size = getpagesize ();
514 caddr_t current_break = (caddr_t) sbrk (0);
517 unexec_error ("unexec() failed because we can't get the size of a page!",
520 /* see if this is a -Bdynamic image -- if so, find ld.so structures */
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!",
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);
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);
540 /* set up some important constants */
543 plt_end = (caddr_t) MASK_UP (ld2->sdt_plt + ld2->sdt_plt_sz, sizeof (double));
545 plt_end = (caddr_t) N_DATADDR (old_hdr);
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);
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);
560 /* set up the new exec */
562 new_edata = (unsigned long) MASK_UP (current_break, n_pagsiz);
563 new_hdr.a_data = new_edata - ((unsigned long) N_DATADDR (old_hdr));
566 /* set up this variable, in case we want to reset "the break"
568 sbrk_of_0_at_unexec = ((unsigned long) MASK_UP (current_break, n_pagsiz));
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. */
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;
581 MASK_UP (current_break, n_pagsiz) - ((unsigned long) current_break);
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));
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);
594 /* pad out the data segment */
595 unexec_pad (new_fd, pad_size);
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);
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);
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
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) +
629 (char *) (new_base_addr + N_STROFF(new_hdr)),
630 new_edata, new_edata,
633 /* Now reset the ld.so symbol table. */
636 struct translation_struct ts;
637 struct relocation_info *tmp, *end;
638 caddr_t syms, strings;
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;
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,
653 new_edata, new_edata,
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));
663 copy_relocation_site (tmp, old_base_addr, new_base_addr, &ts);
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);
679 run_time_remap (char *dummy)
681 unsigned long current_sbrk = (unsigned long) sbrk (0);
683 #if __FreeBSD_version < 300000 /* 2.x can work with this code */
684 if (sbrk_of_0_at_unexec < current_sbrk)
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);
692 if (sbrk_of_0_at_unexec > current_sbrk)
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);
701 /* with proper COW, i don't think we really need to do this... */
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;
711 if (mprotect ((caddr_t) base_addr, len, PROT_READ | PROT_EXEC))
712 fprintf (stderr, "failed to change protection on data pages: %s\n",