Add support for large files, 64-bit Solaris, system locale codings.
[bpt/emacs.git] / src / unexelf.c
index c020d52..804e86b 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1985, 1986, 1987, 1988, 1990, 1992
+/* Copyright (C) 1985, 1986, 1987, 1988, 1990, 1992, 1999
    Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
@@ -412,6 +412,13 @@ Filesz      Memsz       Flags       Align
 
  */
 \f
+#ifndef emacs
+#define fatal(a, b, c) fprintf (stderr, a, b, c), exit (1)
+#else
+#include <config.h>
+extern void fatal (char *, ...);
+#endif
+
 #include <sys/types.h>
 #include <stdio.h>
 #include <sys/stat.h>
@@ -427,12 +434,10 @@ Filesz      Memsz       Flags       Align
 #if defined (__sony_news) && defined (_SYSTYPE_SYSV)
 #include <sys/elf_mips.h>
 #include <sym.h>
-#define HAS_SBSS_SECTION
 #endif /* __sony_news && _SYSTYPE_SYSV */
-
-#if defined (__NetBSD__) && defined (__powerpc__)
-#define HAS_SBSS_SECTION
-#endif
+#if __sgi
+#include <sym.h> /* for HDRR declaration */
+#endif /* __sgi */
 
 #if defined (__alpha__) && !defined (__NetBSD__) && !defined (__OpenBSD__)
 /* Declare COFF debugging symbol table.  This used to be in
@@ -519,17 +524,18 @@ typedef struct {
 
 #ifndef ElfW
 # ifdef __STDC__
-#  define ElfW(type)   Elf32_##type
+#  define ElfBitsW(bits, type) Elf##bits##_##type
 # else
-#  define ElfW(type)   Elf32_/**/type
+#  define ElfBitsW(bits, type) Elf/**/bits/**/_/**/type
 # endif
-#endif
-
-#ifndef emacs
-#define fatal(a, b, c) fprintf (stderr, a, b, c), exit (1)
-#else
-#include <config.h>
-extern void fatal (char *, ...);
+# ifdef _LP64
+#  define ELFSIZE 64
+# else
+#  define ELFSIZE 32
+# endif
+  /* This macro expands `bits' before invoking ElfBitsW.  */
+# define ElfExpandBitsW(bits, type) ElfBitsW (bits, type)
+# define ElfW(type) ElfExpandBitsW (ELFSIZE, type)
 #endif
 
 #ifndef ELF_BSS_SECTION_NAME
@@ -539,7 +545,7 @@ extern void fatal (char *, ...);
 /* Get the address of a particular section or program header entry,
  * accounting for the size of the entries.
  */
-/* 
+/*
    On PPC Reference Platform running Solaris 2.5.1
    the plt section is also of type NOBI like the bss section.
    (not really stored) and therefore sections after the bss
@@ -548,7 +554,7 @@ extern void fatal (char *, ...);
    Thus, we modify the test from
       if (NEW_SECTION_H (nn).sh_offset >= new_data2_offset)
    to
-      if (NEW_SECTION_H (nn).sh_offset >= 
+      if (NEW_SECTION_H (nn).sh_offset >=
                OLD_SECTION_H (old_bss_index-1).sh_offset)
    This is just a hack. We should put the new data section
    before the .plt section.
@@ -580,7 +586,7 @@ typedef unsigned char byte;
 
 /* Round X up to a multiple of Y.  */
 
-ElfW(Addr)
+static ElfW(Addr)
 round_up (x, y)
      ElfW(Addr) x, y;
 {
@@ -625,9 +631,7 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
   ElfW(Addr) new_data2_addr;
 
   int n, nn, old_bss_index, old_data_index, new_data2_index;
-#if defined (HAS_SBSS_SECTION)
   int old_sbss_index, old_mdebug_index;
-#endif /* HAS_SBSS_SECTION */
   struct stat stat_buf;
 
   /* Open the old file & map it into the address space. */
@@ -677,7 +681,6 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
   if (old_bss_index == old_file_h->e_shnum)
     fatal ("Can't find .bss in %s.\n", old_name, 0);
 
-#if defined (HAS_SBSS_SECTION)
   for (old_sbss_index = 1; old_sbss_index < (int) old_file_h->e_shnum;
        old_sbss_index++)
     {
@@ -691,6 +694,7 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
     }
   if (old_sbss_index == old_file_h->e_shnum)
     {
+      old_sbss_index = -1;
       old_bss_addr = OLD_SECTION_H(old_bss_index).sh_addr;
       old_bss_size = OLD_SECTION_H(old_bss_index).sh_size;
       new_data2_offset = OLD_SECTION_H(old_bss_index).sh_offset;
@@ -718,10 +722,7 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
     }
     if (old_mdebug_index == old_file_h->e_shnum)
        old_mdebug_index = 0;
-#else /* not HAS_SBSS_SECTION */           
-  old_bss_addr = OLD_SECTION_H (old_bss_index).sh_addr;
-  old_bss_size = OLD_SECTION_H (old_bss_index).sh_size;
-#endif /* not HAS_SBSS_SECTION */          
+
 #if defined (emacs) || !defined (DEBUG)
   new_bss_addr = (ElfW(Addr)) sbrk (0);
 #else
@@ -729,9 +730,6 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
 #endif
   new_data2_addr = old_bss_addr;
   new_data2_size = new_bss_addr - old_bss_addr;
-#if !defined (HAS_SBSS_SECTION)
-  new_data2_offset = OLD_SECTION_H (old_bss_index).sh_offset;
-#endif /* not HAS_SBSS_SECTION */
 
 #ifdef DEBUG
   fprintf (stderr, "old_bss_index %d\n", old_bss_index);
@@ -812,18 +810,22 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
   for (n = new_file_h->e_phnum - 1; n >= 0; n--)
     {
       /* Compute maximum of all requirements for alignment of section.  */
-      int alignment = (NEW_PROGRAM_H (n)).p_align;
+      ElfW(Word) alignment = (NEW_PROGRAM_H (n)).p_align;
       if ((OLD_SECTION_H (old_bss_index)).sh_addralign > alignment)
        alignment = OLD_SECTION_H (old_bss_index).sh_addralign;
 
-#if defined (HAS_SBSS_SECTION)
+#ifdef __mips
+         /* According to r02kar@x4u2.desy.de (Karsten Kuenne)
+            and oliva@gnu.org (Alexandre Oliva), on IRIX 5.2, we
+            always get "Program segment above .bss" when dumping
+            when the executable doesn't have an sbss section.  */
+      if (old_sbss_index != -1)
+#endif /* __mips */
       if (NEW_PROGRAM_H (n).p_vaddr + NEW_PROGRAM_H (n).p_filesz
-         > round_up (old_bss_addr, alignment))
-       fatal ("Program segment above .bss in %s\n", old_name, 0);
-#else /* not HAS_SBSS_SECTION */
-      if (NEW_PROGRAM_H (n).p_vaddr + NEW_PROGRAM_H (n).p_filesz > old_bss_addr)
-       fatal ("Program segment above .bss in %s\n", old_name, 0);
-#endif /* not HAS_SBSS_SECTION */
+         > (old_sbss_index == -1
+            ? old_bss_addr
+            : round_up (old_bss_addr, alignment)))
+         fatal ("Program segment above .bss in %s\n", old_name, 0);
 
       if (NEW_PROGRAM_H (n).p_type == PT_LOAD
          && (round_up ((NEW_PROGRAM_H (n)).p_vaddr
@@ -871,17 +873,10 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
   for (n = 1, nn = 1; n < (int) old_file_h->e_shnum; n++, nn++)
     {
       caddr_t src;
-      int temp_index;
-#if defined (HAS_SBSS_SECTION)
       /* If it is (s)bss section, insert the new data2 section before it.  */
       /* new_data2_index is the index of either old_sbss or old_bss, that was
         chosen as a section for new_data2.   */
-      temp_index = new_data2_index;
-#else /* not HAS_SBSS_SECTION */
-      /* If it is bss section, insert the new data2 section before it.  */
-      temp_index = old_bss_index;
-#endif /* not HAS_SBSS_SECTION */
-      if (n == temp_index)
+      if (n == new_data2_index)
        {
          /* Steal the data section header for this data2 section. */
          memcpy (&NEW_SECTION_H (nn), &OLD_SECTION_H (old_data_index),
@@ -906,16 +901,16 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
              old_file_h->e_shentsize);
       
       if (n == old_bss_index
-#if defined (HAS_SBSS_SECTION)
          /* The new bss and sbss section's size is zero, and its file offset
             and virtual address should be off by NEW_DATA2_SIZE.  */
          || n == old_sbss_index
-#endif /* HAS_SBSS_SECTION */
          )
        {
-         /* NN should be `old_bss_index + 1' at this point. */
-         NEW_SECTION_H (nn).sh_offset += new_data2_size;
-         NEW_SECTION_H (nn).sh_addr += new_data2_size;
+         /* NN should be `old_s?bss_index + 1' at this point. */
+         NEW_SECTION_H (nn).sh_offset =
+           NEW_SECTION_H (new_data2_index).sh_offset + new_data2_size;
+         NEW_SECTION_H (nn).sh_addr =
+           NEW_SECTION_H (new_data2_index).sh_addr + new_data2_size;
          /* Let the new bss section address alignment be the same as the
             section address alignment followed the old bss section, so
             this section will be placed in exactly the same place. */
@@ -939,9 +934,15 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
              >= OLD_SECTION_H (old_bss_index-1).sh_offset)
            NEW_SECTION_H (nn).sh_offset += new_data2_size;
 #else
-         if (round_up (NEW_SECTION_H (nn).sh_offset,
+         /* The idea of this is that the bss section's sh_offset
+            may need rounding up to compare with new_data2_offset.
+            So we cannot simply compare the sh_offset.
+            However, another small section could exist just before
+            the bss section, and we need to know that is before.  */
+         if (round_up (NEW_SECTION_H (nn).sh_offset
+                       + NEW_SECTION_H (nn).sh_size,
                        OLD_SECTION_H (old_bss_index).sh_addralign)
-             >= new_data2_offset)
+             > new_data2_offset)
            NEW_SECTION_H (nn).sh_offset += new_data2_size;
 #endif
          /* Any section that was originally placed after the section
@@ -971,18 +972,16 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
         ".data" in the strings table) get copied from the current process
         instead of the old file.  */
       if (!strcmp (old_section_names + NEW_SECTION_H (n).sh_name, ".data")
-#ifdef _nec_ews_svr4                           /* hir, 1994.6.13 */
-         || !strcmp ((old_section_names + NEW_SECTION_H(n).sh_name),
-                     ".sdata")
-#endif
-#if defined (HAS_SBSS_SECTION)
          || !strcmp ((old_section_names + NEW_SECTION_H (n).sh_name),
                      ".sdata")
          || !strcmp ((old_section_names + NEW_SECTION_H (n).sh_name),
                      ".lit4")
          || !strcmp ((old_section_names + NEW_SECTION_H (n).sh_name),
                      ".lit8")
-#endif /* HAS_SBSS_SECTION */
+         || !strcmp ((old_section_names + NEW_SECTION_H (n).sh_name),
+                     ".got")
+         || !strcmp ((old_section_names + NEW_SECTION_H (n).sh_name),
+                     ".sdata1")
          || !strcmp ((old_section_names + NEW_SECTION_H (n).sh_name),
                      ".data1"))
        src = (caddr_t) OLD_SECTION_H (n).sh_addr;
@@ -1036,6 +1035,62 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
            }
        }
 #endif /* __sony_news && _SYSTYPE_SYSV */
+
+#if __sgi
+      /* Adjust  the HDRR offsets in .mdebug and copy the 
+        line data if it's in its usual 'hole' in the object.
+        Makes the new file debuggable with dbx.
+        patches up two problems: the absolute file offsets
+        in the HDRR record of .mdebug (see /usr/include/syms.h), and
+        the ld bug that gets the line table in a hole in the
+        elf file rather than in the .mdebug section proper.
+        David Anderson. davea@sgi.com  Jan 16,1994.  */
+      if (n == old_mdebug_index)
+       {
+#define MDEBUGADJUST(__ct,__fileaddr)          \
+  if (n_phdrr->__ct > 0)                       \
+    {                                          \
+      n_phdrr->__fileaddr += movement;         \
+    }
+
+         HDRR * o_phdrr = (HDRR *)((byte *)old_base + OLD_SECTION_H (n).sh_offset);
+         HDRR * n_phdrr = (HDRR *)((byte *)new_base + NEW_SECTION_H (nn).sh_offset);
+         unsigned movement = new_data2_size;
+
+         MDEBUGADJUST (idnMax, cbDnOffset);
+         MDEBUGADJUST (ipdMax, cbPdOffset);
+         MDEBUGADJUST (isymMax, cbSymOffset);
+         MDEBUGADJUST (ioptMax, cbOptOffset);
+         MDEBUGADJUST (iauxMax, cbAuxOffset);
+         MDEBUGADJUST (issMax, cbSsOffset);
+         MDEBUGADJUST (issExtMax, cbSsExtOffset);
+         MDEBUGADJUST (ifdMax, cbFdOffset);
+         MDEBUGADJUST (crfd, cbRfdOffset);
+         MDEBUGADJUST (iextMax, cbExtOffset);
+         /* The Line Section, being possible off in a hole of the object,
+            requires special handling.  */
+         if (n_phdrr->cbLine > 0)
+           {
+             if (o_phdrr->cbLineOffset > (OLD_SECTION_H (n).sh_offset
+                                          + OLD_SECTION_H (n).sh_size))
+               {
+                 /* line data is in a hole in elf. do special copy and adjust
+                    for this ld mistake.
+                    */
+                 n_phdrr->cbLineOffset += movement;
+
+                 memcpy (n_phdrr->cbLineOffset + new_base,
+                         o_phdrr->cbLineOffset + old_base, n_phdrr->cbLine);
+               }
+             else
+               {
+                 /* somehow line data is in .mdebug as it is supposed to be.  */
+                 MDEBUGADJUST (cbLine, cbLineOffset);
+               }
+           }
+       }
+#endif /* __sgi */
+
       /* If it is the symbol table, its st_shndx field needs to be patched.  */
       if (NEW_SECTION_H (nn).sh_type == SHT_SYMTAB
          || NEW_SECTION_H (nn).sh_type == SHT_DYNSYM)
@@ -1094,6 +1149,16 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
           member.  */
        nn = section.sh_info;
        if (!strcmp (old_section_names + NEW_SECTION_H (nn).sh_name, ".data")
+           || !strcmp ((old_section_names + NEW_SECTION_H (nn).sh_name),
+                       ".sdata")
+           || !strcmp ((old_section_names + NEW_SECTION_H (nn).sh_name),
+                       ".lit4")
+           || !strcmp ((old_section_names + NEW_SECTION_H (nn).sh_name),
+                       ".lit8")
+           || !strcmp ((old_section_names + NEW_SECTION_H (nn).sh_name),
+                       ".got")
+           || !strcmp ((old_section_names + NEW_SECTION_H (nn).sh_name),
+                       ".sdata1")
            || !strcmp ((old_section_names + NEW_SECTION_H (nn).sh_name),
                        ".data1"))
          {