/* 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.
#include <string.h>
#else
#include <config.h>
-extern void fatal (char *, ...);
+extern void fatal (const char *msgid, ...);
#endif
#include <sys/types.h>
#if __sgi
#include <syms.h> /* for HDRR declaration */
#endif /* __sgi */
+#ifdef BROKEN_NOCOMBRELOC
+#include <assert.h>
+#endif
#ifndef MAP_ANON
#ifdef MAP_ANONYMOUS
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. */
#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,
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);
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;
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);
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. */
&& 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)
/* 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);
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);