]> code.delx.au - gnu-emacs/blobdiff - src/unexelf.c
(archive-l-e): New optional argument `float' means generate a float value.
[gnu-emacs] / src / unexelf.c
index ee563b36a9701e3ae86db59b98b33cc646348db2..a236b98eab112c6d8de26f196432d146d46ad2f5 100644 (file)
@@ -1,5 +1,5 @@
 /* Copyright (C) 1985, 1986, 1987, 1988, 1990, 1992, 1999, 2000, 2001,
-                 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+                 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -412,7 +412,7 @@ temacs:
 #include <string.h>
 #else
 #include <config.h>
-extern void fatal (char *, ...);
+extern void fatal (const char *msgid, ...);
 #endif
 
 #include <sys/types.h>
@@ -433,6 +433,9 @@ extern void fatal (char *, ...);
 #if __sgi
 #include <syms.h> /* for HDRR declaration */
 #endif /* __sgi */
+#ifdef BROKEN_NOCOMBRELOC
+#include <assert.h>
+#endif
 
 #ifndef MAP_ANON
 #ifdef MAP_ANONYMOUS
@@ -682,11 +685,14 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
   ElfW(Addr) new_data2_addr;
 
   int n, nn;
-  int old_bss_index, old_sbss_index;
+  int old_bss_index, old_sbss_index, old_plt_index;
   int old_data_index, new_data2_index;
   int old_mdebug_index;
   struct stat stat_buf;
   int old_file_size;
+#ifdef BROKEN_NOCOMBRELOC
+  int unreloc_sections[10], n_unreloc_sections;
+#endif
 
   /* Open the old file, allocate a buffer of the right size, and read
      in the file contents.  */
@@ -702,7 +708,7 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
 #if MAP_ANON == 0
   mmap_fd = open ("/dev/zero", O_RDONLY);
   if (mmap_fd < 0)
-    fatal ("Can't open /dev/zero for reading: errno %d\n", errno);
+    fatal ("Can't open /dev/zero for reading: errno %d\n", errno, 0);
 #endif
 
   /* We cannot use malloc here because that may use sbrk.  If it does,
@@ -713,7 +719,7 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
   old_base = mmap (NULL, old_file_size, PROT_READ | PROT_WRITE,
                   MAP_ANON | MAP_PRIVATE, mmap_fd, 0);
   if (old_base == MAP_FAILED)
-    fatal ("Can't allocate buffer for %s\n", old_name);
+    fatal ("Can't allocate buffer for %s\n", old_name, 0);
 
   if (read (old_file, old_base, stat_buf.st_size) != stat_buf.st_size)
     fatal ("Didn't read all of %s: errno %d\n", old_name, errno);
@@ -740,15 +746,34 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
   old_sbss_index = find_section (".sbss", old_section_names,
                                 old_name, old_file_h, old_section_h, 1);
   if (old_sbss_index != -1)
-    if (OLD_SECTION_H (old_sbss_index).sh_type == SHT_PROGBITS)
+    if (OLD_SECTION_H (old_sbss_index).sh_type != SHT_NOBITS)
       old_sbss_index = -1;
 
-  if (old_sbss_index == -1)
+  /* PowerPC64 has .plt in the BSS section.  */
+  old_plt_index = find_section (".plt", old_section_names,
+                               old_name, old_file_h, old_section_h, 1);
+  if (old_plt_index != -1)
+    if (OLD_SECTION_H (old_plt_index).sh_type != SHT_NOBITS)
+      old_plt_index = -1;
+
+  if (old_sbss_index == -1 && old_plt_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_index = old_bss_index;
     }
+  else if (old_plt_index != -1
+          && (old_sbss_index == -1
+              || (OLD_SECTION_H (old_sbss_index).sh_addr
+                  > OLD_SECTION_H (old_plt_index).sh_addr)))
+    {
+      old_bss_addr = OLD_SECTION_H (old_plt_index).sh_addr;
+      old_bss_size = OLD_SECTION_H (old_bss_index).sh_size
+       + OLD_SECTION_H (old_plt_index).sh_size;
+      if (old_sbss_index != -1)
+       old_bss_size += OLD_SECTION_H (old_sbss_index).sh_size;
+      new_data2_index = old_plt_index;
+    }
   else
     {
       old_bss_addr = OLD_SECTION_H (old_sbss_index).sh_addr;
@@ -802,7 +827,7 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
   new_base = mmap (NULL, new_file_size, PROT_READ | PROT_WRITE,
                   MAP_ANON | MAP_PRIVATE, mmap_fd, 0);
   if (new_base == MAP_FAILED)
-    fatal ("Can't allocate buffer for %s\n", old_name);
+    fatal ("Can't allocate buffer for %s\n", old_name, 0);
 
   new_file_h = (ElfW(Ehdr) *) new_base;
   new_program_h = (ElfW(Phdr) *) ((byte *) new_base + old_file_h->e_phoff);
@@ -934,7 +959,7 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
       if (n == old_bss_index
          /* 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
+         || n == old_sbss_index || n == old_plt_index
          )
        {
          /* NN should be `old_s?bss_index + 1' at this point. */
@@ -1079,7 +1104,7 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
          && old_mdebug_index != -1)
         {
          int diff = NEW_SECTION_H(nn).sh_offset
-               - OLD_SECTION_H(old_mdebug_index).sh_offset;
+               - OLD_SECTION_H(old_mdebug_index).sh_offset;
          HDRR *phdr = (HDRR *)(NEW_SECTION_H (nn).sh_offset + new_base);
 
          if (diff)
@@ -1199,6 +1224,7 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
 
   /* This loop seeks out relocation sections for the data section, so
      that it can undo relocations performed by the runtime linker.  */
+#ifndef BROKEN_NOCOMBRELOC
   for (n = new_file_h->e_shnum - 1; n; n--)
     {
       ElfW(Shdr) section = NEW_SECTION_H (n);
@@ -1253,13 +1279,92 @@ unexec (new_name, old_name, data_start, bss_start, entry_address)
          break;
        }
     }
+#else /* BROKEN_NOCOMBRELOC */
+  for (n = 1, n_unreloc_sections = 0; n < new_file_h->e_shnum; n++)
+    if (!strcmp (old_section_names + NEW_SECTION_H (n).sh_name, ".data")
+       || !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")
+#ifdef IRIX6_5                 /* see above */
+       || !strcmp (old_section_names + NEW_SECTION_H (n).sh_name, ".got")
+#endif
+       || !strcmp (old_section_names + NEW_SECTION_H (n).sh_name, ".sdata1")
+       || !strcmp (old_section_names + NEW_SECTION_H (n).sh_name, ".data1"))
+      {
+       assert (n_unreloc_sections
+               < (sizeof (unreloc_sections) / sizeof (unreloc_sections[0])));
+       unreloc_sections[n_unreloc_sections++] = n;
+#ifdef DEBUG
+       fprintf (stderr, "section %d: %s\n", n,
+                old_section_names + NEW_SECTION_H (n).sh_name);
+#endif
+      }
+
+  for (n = new_file_h->e_shnum - 1; n; n--)
+    {
+      ElfW(Shdr) section = NEW_SECTION_H (n);
+      caddr_t reloc, end;
+      ElfW(Addr) addr, offset;
+      int target;
+
+      switch (section.sh_type)
+       {
+       default:
+         break;
+       case SHT_REL:
+       case SHT_RELA:
+         /* This code handles two different size structs, but there should
+            be no harm in that provided that r_offset is always the first
+            member.  */
+         for (reloc = old_base + section.sh_offset,
+                end = reloc + section.sh_size;
+              reloc < end;
+              reloc += section.sh_entsize)
+           {
+             addr = ((ElfW(Rel) *) reloc)->r_offset;
+#ifdef __alpha__
+             /* The Alpha ELF binutils currently have a bug that
+                sometimes results in relocs that contain all
+                zeroes.  Work around this for now...  */
+             if (addr == 0)
+               continue;
+#endif
+             for (nn = 0; nn < n_unreloc_sections; nn++)
+               {
+                 target = unreloc_sections[nn];
+                 if (NEW_SECTION_H (target).sh_addr <= addr
+                     && addr < (NEW_SECTION_H (target).sh_addr +
+                                NEW_SECTION_H (target).sh_size))
+                   {
+                     offset = (NEW_SECTION_H (target).sh_addr -
+                               NEW_SECTION_H (target).sh_offset);
+                     memcpy (new_base + addr - offset,
+                             old_base + addr - offset,
+                             sizeof (ElfW(Addr)));
+#ifdef DEBUG
+                     fprintf (stderr, "unrelocate: [%08lx] <= %08lx\n",
+                              (long) addr,
+                              (long) *((long *) (new_base + addr - offset)));
+#endif
+                     break;
+                   }
+               }
+           }
+         break;
+       }
+    }
+#endif /* BROKEN_NOCOMBRELOC */
 
   /* Write out new_file, and free the buffers.  */
 
   if (write (new_file, new_base, new_file_size) != new_file_size)
+#ifndef emacs
+    fatal ("Didn't write %d bytes: errno %d\n",
+          new_file_size, errno);
+#else
     fatal ("Didn't write %d bytes to %s: errno %d\n",
           new_file_size, new_base, errno);
-
+#endif
   munmap (old_base, old_file_size);
   munmap (new_base, new_file_size);