jeffrey@algor2.UUCP (Jeffrey Kegler) (07/06/89)
On my system (80386 running Bell Tech or Intel S5R3.2u) emacs will not dump successfully when compiled using one of the shared libraries. Alas, I only have X11 in shared library form. Is there a way to get dumping and shared libraries to coexist? -- Jeffrey Kegler, President, Algorists, jeffrey@algor2.UU.NET or uunet!algor2!jeffrey 1762 Wainwright DR, Reston VA 22090
james@bigtex.cactus.org (James Van Artsdalen) (07/09/89)
In <467@algor2.UUCP>, jeffrey@algor2.UUCP (Jeffrey Kegler) wrote: > On my system (80386 running Bell Tech or Intel S5R3.2u) emacs will not > dump successfully when compiled using one of the shared libraries. The immediate problem is likely that the emacs unexec() function is not emitting the right COFF sections. In addition to .text, .data and .bss, a binary linked with shared libraries also has .lib, .fka?00 and .fka?40 sections (the ? depends on which shared library is referenced: each shared library referenced has its own .fka?00 and .fka?40 in the COFF file). I believe that the .lib section contains the paths to the shared libraries themselves, and suspect that the .fka* sections map the code & data of the shared library into the process address space. > Alas, I only have X11 in shared library form. Is there a way to get > dumping and shared libraries to coexist? I've managed to generate an xemacs that uses shared libraries that the kernel will exec(2). It took a number of kludgy hard-coded hacks to unexec.c, as the standard unexec doesn't copy over sections it doesn't understand (ie, .lib and .fka*). This xemacs actually runs in emacs.c for a while. Unfortunately, the next problem is much harder, and probably prevents any good solution. In COFF .o files there can be a section called ".init". When linking, all .init sections appear to be simply concatinated together and placed at the front of the emitted .text section. This allows library routines to get control for initialization and program startup. No .init section actually appears in the executable, as the code really is in the .text section. I suspect .init would be most useful for g++. The killer is that malloc() appears to use the .init section to initialize a variable called _libc_end (kept in the high address space used for the libc_s shared library, not in .data or .bss) that is apparently a pointer to the base of the .bss section. In addition, other variables whose function I haven't figured out are initialized too (this is all derived from disassembly and "nm /lib/libc_s.a"). Here's the section header dump from a temacs compiled with libc_s: ***SECTION HEADER*** Name Paddr Vaddr Scnptr Relptr Lnnoptr Flags Size Nreloc Nlnno temacs: .text 0x00000148 0x00000148 0x00000148 0x00000000 0x00072604 0x00000020 0x00049d64 0 193 .data 0x00400eac 0x00400eac 0x00049eac 0x00000000 0x00000000 0x00000040 0x0002423c 0 0 .bss 0x004250e8 0x004250e8 0x00000000 0x00000000 0x00000000 0x00000080 0x00006320 0 0 .fka000 0xa0000000 0xa0000000 0x00000000 0x00000000 0x00000000 0x00000022 0x00004b98 0 0 .fka040 0xa0400000 0xa0400000 0x00000000 0x00000000 0x00000000 0x00000042 0x00000628 0 0 .comment 0x00000000 0x00000000 0x0006e0e8 0x00000000 0x00000000 0x00000200 0x00004504 0 0 .lib 0x00000001 0x00000000 0x000725ec 0x00000000 0x00000000 0x00000800 0x00000018 0 0 My modified unexec() copies over the .fka0?0 section headers and the .lib header and data (.fka* have no section data). Here's what a disassembly of the first few instructions executed in temacs looks like: disassembly for temacs section .text _istart() 148: c7 05 04 00 40 a0 00 4a 42 00 movl $0x424a00,0xa0400004 152: 90 nop 153: 90 nop 154: c7 05 18 00 40 a0 18 ae 42 00 movl $0x42ae18,0xa0400018 15e: c7 05 1c 00 40 a0 8c 8f 42 00 movl $0x428f8c,0xa040001c 168: c7 05 20 00 40 a0 20 b2 42 00 movl $0x42b220,0xa0400020 172: c7 05 24 00 40 a0 28 4c 42 00 movl $0x424c28,0xa0400024 17c: c7 05 28 00 40 a0 e8 4f 42 00 movl $0x424fe8,0xa0400028 186: c7 05 2c 00 40 a0 ec 4f 42 00 movl $0x424fec,0xa040002c 190: c7 05 34 00 40 a0 0f 00 00 a0 movl $0xa000000f,0xa0400034 19a: c7 05 30 00 40 a0 08 b4 42 00 movl $0x42b408,0xa0400030 1a4: c7 05 38 00 40 a0 b0 0e 40 00 movl $0x400eb0,0xa0400038 1ae: 90 nop 1af: 90 nop 1b0: c7 05 08 00 40 a0 94 3b 04 00 movl $0x43b94,0xa0400008 1ba: 90 nop 1bb: 90 nop 1bc: c7 05 10 00 40 a0 24 3c 04 00 movl $0x43c24,0xa0400010 1c6: c7 05 14 00 40 a0 e0 50 42 00 movl $0x4250e0,0xa0400014 1d0: c3 ret 1d1: 90 nop 1d2: 90 nop 1d3: 90 nop gcc_compiled.() _start() 1d4: 83 ec 08 subl $0x8,%esp _start() is the traditional /lib/crt0.o start, and istart() is conjured up by the linker for the .init stuff: everything between _istart() and _start() came from .init sections of various routines in the C library (_start() is the true entry point, and it calls istart() though I don't show that call here). The movl at address 19a is the likely culprit: it appears to want to point to the first byte after .bss. After undumping, this constant is no longer valid because the data space as a whole grew. I tried to add the statement extern int _libc_end; _libc_end = sbrk(0); to main() in emacs.c, but it turns out that sbrk(2) isn't really a kernel call: it just adds a remembered constant to the argument and calls brk(2). The remembered constant appears derived from _libc_end, so since there appears to be no other way of telling where the end of .bss is, unexec() will probably have to search the start of the text region for the movl and patch the correct constant... :-( If anyone has any questions/comments/suggestions, please let me know. -- James R. Van Artsdalen james@bigtex.cactus.org "Live Free or Die" Dell Computer Co 9505 Arboretum Blvd Austin TX 78759 512-338-8789
james@bigtex.cactus.org (James Van Artsdalen) (07/10/89)
In <18854@bigtex.cactus.org>, james@bigtex.cactus.org (That's Me) wrote: > This xemacs actually runs in emacs.c for a while. > Unfortunately, the next problem is much harder, and probably prevents > any good solution. It may not be good, but here are patches to allow one to unexec with shared libraries and run for more than "a while". The changes are as hacky as unexec.c ever was, so no guaranteeing that it works: I don't recommend trying it unless you're willing to use gdb and fix it. I have tested the result with and without shared libraries, and with gcc and pcc. I am using s-usg5-3.h and m-intel386.h. Add this mess to the end of your config.h file. USG_SHARED_LIBRARIES is what flags the use of shared libraries. Only -lc_s goes here now, because various other places know about -lX11_s and -lnsl_s. The alloca() and GNULIB are for compiling emacs with GNU C. The stuff at the very end is for changing the libraries and start files that xmakefile will want to use. #define USG_SHARED_LIBRARIES -lc_s #if defined(__GNUC__) && !defined(alloca) #define alloca(n) __builtin_alloca(n) #endif #ifdef __GNUC__ #define GNULIB /nfs/raid/lib/gcc-gnulib #else #define GNULIB #endif #undef LIB_STANDARD #ifdef USG_SHARED_LIBRARIES #ifdef X11 #define LIB_X11_LIB -lX11_s #endif /* X11 */ #define START_FILES pre-crt0.o /lib/crt1.o #define LIB_STANDARD -linet USG_SHARED_LIBRARIES GNULIB -lPW -lg -lc \ /lib/crtn.o #else /* USG_SHARED_LIBRARIES */ #define LIB_STANDARD -linet -lPW -lg -lc GNULIB #endif /* USG_SHARED_LIBRARIES */ ------------------------------------------------------------------------------- Here are the patches. The change to emacs.c is to handle the sbrk(0) problem: without it sbrk(0) returns the wrong thing and confuses malloc(3c). The changes to ymakefile is so that the X library can have a name other than libX11.a (which it will if shared). The makefile #define is so that my config.h will not define certain inline asm functions not shown here. In unexec.c, "bias" is calculated differently - I couldn't make the old definition work. The basic algorithm is to scan the section headers in the original file once find the text, data and bss headers (and to find the end of section data), again to write all section headers in the right order, and again to write the section data in the right order. Any unrecognized sections will be simply written out as is. I probably shouldn't have renamed copy_text_and_data, but I thought the result would be different than it turned out to be. The biggest non-obvious limitation is that I don't believe that the unexec() changes will work if the text section isn't first, or if anything comes between text, data & bss. *** /tmp/,RCSt1a01031 Sun Jul 9 21:36:34 1989 --- emacs.c Sun Jul 9 20:43:33 1989 *************** *** 81,86 **** --- 81,88 ---- int xargc; #endif /* HAVE_X_WINDOWS */ + unsigned int bss_end = 0; + /* Nonzero means running Emacs without interactive terminal. */ int noninteractive; *************** *** 202,207 **** --- 204,214 ---- #endif /* SHAREABLE_LIB_BUG */ #endif /* LINK_CRTL_SHARE */ #endif /* VMS */ + + #ifdef USG_SHARED_LIBRARIES + if (bss_end) + brk(bss_end); + #endif clearerr (stdin); *** /tmp/,RCSt1a01031 Sun Jul 9 21:36:36 1989 --- ymakefile Sun Jul 9 20:46:19 1989 *************** *** 27,32 **** --- 27,33 ---- cppdir = ../cpp/ #define NO_SHORTNAMES + #define MAKEFILE #include "config.h" /* Use HAVE_X11 as an alias for X11 in this file *************** *** 38,43 **** --- 39,48 ---- #undef X11 #endif + #ifndef LIB_X11_LIB + #define LIB_X11_LIB -lX11 + #endif + /* On some machines #define register is done in config; don't let it interfere with this file. */ #undef register *************** *** 173,182 **** #ifdef HAVE_X11 #ifdef HAVE_X_MENU XOBJ = x11term.o x11fns.o xmenu.o ! LIBX = -lXMenu -loldX -lX11 LIBX11_MACHINE LIBX11_SYSTEM #else XOBJ = x11term.o x11fns.o ! LIBX = -lX11 LIBX11_MACHINE LIBX11_SYSTEM #endif #else /* not HAVE_X11 */ #ifdef HAVE_X_MENU --- 178,187 ---- #ifdef HAVE_X11 #ifdef HAVE_X_MENU XOBJ = x11term.o x11fns.o xmenu.o ! LIBX = -lXMenu -loldX LIB_X11_LIB LIBX11_MACHINE LIBX11_SYSTEM #else XOBJ = x11term.o x11fns.o ! LIBX = LIB_X11_LIB LIBX11_MACHINE LIBX11_SYSTEM #endif #else /* not HAVE_X11 */ #ifdef HAVE_X_MENU *** /tmp/,RCSt1a01031 Sun Jul 9 21:36:37 1989 --- unexec.c Sun Jul 9 21:35:51 1989 *************** *** 400,406 **** } if (make_hdr (new, a_out, data_start, bss_start, entry_address, a_name, new_name) < 0 ! || copy_text_and_data (new) < 0 || copy_sym (new, a_out, a_name, new_name) < 0 #ifdef COFF || adjust_lnnoptrs (new, a_out, new_name) < 0 --- 400,406 ---- } if (make_hdr (new, a_out, data_start, bss_start, entry_address, a_name, new_name) < 0 ! || copy_sections (new, a_out) < 0 || copy_sym (new, a_out, a_name, new_name) < 0 #ifdef COFF || adjust_lnnoptrs (new, a_out, new_name) < 0 *************** *** 440,446 **** auto struct scnhdr scntemp; /* Temporary section header */ register int scns; #endif /* COFF */ ! unsigned int bss_end; pagemask = getpagesize () - 1; --- 440,446 ---- auto struct scnhdr scntemp; /* Temporary section header */ register int scns; #endif /* COFF */ ! extern unsigned int bss_end; pagemask = getpagesize () - 1; *************** *** 533,538 **** --- 533,541 ---- /* Now we alter the contents of all the f_*hdr variables to correspond to what we want to dump. */ + bias = (bss_start - f_ohdr.data_start) + (bss_end - bss_start) - + f_dhdr.s_size; + f_hdr.f_flags |= (F_RELFLG | F_EXEC); #ifdef EXEC_MAGIC f_ohdr.magic = EXEC_MAGIC; *************** *** 576,582 **** f_bhdr.s_vaddr = f_ohdr.data_start + f_ohdr.dsize; f_bhdr.s_size = f_ohdr.bsize; f_bhdr.s_scnptr = 0L; - bias = f_dhdr.s_scnptr + f_dhdr.s_size - block_copy_start; if (f_hdr.f_symptr > 0L) { --- 579,584 ---- *************** *** 602,621 **** PERROR (new_name); } ! if (write (new, &f_thdr, sizeof (f_thdr)) != sizeof (f_thdr)) ! { ! PERROR (new_name); ! } ! if (write (new, &f_dhdr, sizeof (f_dhdr)) != sizeof (f_dhdr)) ! { ! PERROR (new_name); ! } - if (write (new, &f_bhdr, sizeof (f_bhdr)) != sizeof (f_bhdr)) - { - PERROR (new_name); - } return (0); #else /* if not COFF */ --- 604,632 ---- PERROR (new_name); } ! lseek(a_out, sizeof f_hdr + sizeof f_ohdr, 0); ! for (scns = f_hdr.f_nscns; scns > 0; scns--) { ! if (read (a_out, &scntemp, sizeof (scntemp)) != sizeof (scntemp)) ! PERROR (a_name); ! ! if (!strcmp(scntemp.s_name, f_thdr.s_name)) { ! if (write (new, &f_thdr, sizeof (f_thdr)) != sizeof (f_thdr)) ! PERROR (new_name); ! } else if (!strcmp(scntemp.s_name, f_dhdr.s_name)) { ! if (write (new, &f_dhdr, sizeof (f_dhdr)) != sizeof (f_dhdr)) ! PERROR (new_name); ! } else if (!strcmp(scntemp.s_name, f_bhdr.s_name)) { ! if (write (new, &f_bhdr, sizeof (f_bhdr)) != sizeof (f_bhdr)) ! PERROR (new_name); ! } else { ! if (scntemp.s_scnptr) ! scntemp.s_scnptr += bias; ! if (write (new, &scntemp, sizeof (scntemp)) != sizeof (scntemp)) ! PERROR (new_name); ! } ! } return (0); #else /* if not COFF */ *************** *** 680,706 **** } /* **************************************************************** ! * copy_text_and_data * ! * Copy the text and data segments from memory to the new a.out */ static int ! copy_text_and_data (new) ! int new; { register char *end; register char *ptr; #ifdef COFF ! lseek (new, (long) text_scnptr, 0); ! ptr = (char *) f_ohdr.text_start; ! end = ptr + f_ohdr.tsize; ! write_segment (new, ptr, end); ! lseek (new, (long) data_scnptr, 0); ! ptr = (char *) f_ohdr.data_start; ! end = ptr + f_ohdr.dsize; ! write_segment (new, ptr, end); #else /* if not COFF */ --- 691,743 ---- } /* **************************************************************** ! * copy_sections * ! * Copy all sections to the new a.out */ static int ! copy_sections (new, a_out) ! int new, a_out; { register char *end; register char *ptr; + register int scns; + auto struct scnhdr scntemp; /* Temporary section header */ #ifdef COFF ! ! lseek(a_out, sizeof(struct filehdr) + sizeof(struct aouthdr), 0); ! for (scns = f_hdr.f_nscns; scns > 0; scns--) { ! if (read (a_out, &scntemp, sizeof (scntemp)) != sizeof (scntemp)) ! PERROR ("temacs"); ! ! if (!strcmp(scntemp.s_name, ".text")) { ! lseek (new, (long) text_scnptr, 0); ! ptr = (char *) f_ohdr.text_start; ! end = ptr + f_ohdr.tsize; ! write_segment (new, ptr, end); ! } else if (!strcmp(scntemp.s_name, ".data")) { ! lseek (new, (long) data_scnptr, 0); ! ptr = (char *) f_ohdr.data_start; ! end = ptr + f_ohdr.dsize; ! write_segment (new, ptr, end); ! } else if (!scntemp.s_scnptr) ! ; /* do nothing - no data for this section */ ! else { ! char page[BUFSIZ]; ! int size, n; ! int old_a_out_ptr = lseek(a_out, 0, 1); ! ! lseek(a_out, scntemp.s_scnptr, 0); ! for (size = scntemp.s_size; size > 0; size -= sizeof page) { ! n = size > sizeof page ? sizeof page : size; ! if (read(a_out, page, n) != n || write(new, page, n) != n) ! PERROR ("xemacs"); ! } ! lseek(a_out, old_a_out_ptr, 0); ! } ! } #else /* if not COFF */ -- James R. Van Artsdalen james@bigtex.cactus.org "Live Free or Die" Dell Computer Co 9505 Arboretum Blvd Austin TX 78759 512-338-8789