#include <sys/types.h>
#include <stdio.h>
#include <sys/stat.h>
#include <memory.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <elf.h>
#include <sys/mman.h>
#include <assert.h>
#ifndef emacs
#define fatal(a, b, c) fprintf(stderr, a, b, c), exit(1)
#else
extern void fatal(char *, ...);
#endif
#define OLD_SECTION_H(n) \
(*(Elf32_Shdr *) ((byte *) old_section_h + old_file_h->e_shentsize * (n)))
#define NEW_SECTION_H(n) \
(*(Elf32_Shdr *) ((byte *) new_section_h + new_file_h->e_shentsize * (n)))
#define OLD_PROGRAM_H(n) \
(*(Elf32_Phdr *) ((byte *) old_program_h + old_file_h->e_phentsize * (n)))
#define NEW_PROGRAM_H(n) \
(*(Elf32_Phdr *) ((byte *) new_program_h + new_file_h->e_phentsize * (n)))
#define PATCH_INDEX(n) \
do { \
if ((n) >= old_sbss_index) \
(n) += 1 + (old_sdata_index ? 0 : 1); } while (0)
typedef unsigned char byte;
int
round_up (x, y)
int x, y;
{
int rem = x % y;
if (rem == 0)
return x;
return x - rem + y;
}
void
unexec (new_name, old_name, data_start, bss_start, entry_address)
char *new_name, *old_name;
unsigned data_start, bss_start, entry_address;
{
extern unsigned int bss_end;
int new_file, old_file, new_file_size;
caddr_t old_base, new_base;
Elf32_Ehdr *old_file_h, *new_file_h;
Elf32_Phdr *old_program_h, *new_program_h;
Elf32_Shdr *old_section_h, *new_section_h;
char *old_section_names;
Elf32_Addr old_bss_addr, new_bss_addr;
Elf32_Addr old_sbss_addr;
Elf32_Word old_bss_size, new_data2_size;
Elf32_Word old_sbss_size, new_data3_size;
Elf32_Off new_data2_offset;
Elf32_Off new_data3_offset;
Elf32_Addr new_data2_addr;
Elf32_Addr new_data3_addr;
Elf32_Addr old_rel_dyn_addr;
Elf32_Word old_rel_dyn_size;
int old_rel_dyn_index;
Elf32_Word old_sdata_size, new_sdata_size;
int old_sdata_index = 0;
int n, nn, old_data_index, new_data2_align;
int old_bss_index;
int old_sbss_index;
int old_bss_padding;
struct stat stat_buf;
old_file = open (old_name, O_RDONLY);
if (old_file < 0)
fatal ("Can't open %s for reading: errno %d\n", old_name, errno);
if (fstat (old_file, &stat_buf) == -1)
fatal ("Can't fstat(%s): errno %d\n", old_name, errno);
old_base = mmap (0, stat_buf.st_size, PROT_READ, MAP_SHARED, old_file, 0);
if (old_base == (caddr_t) -1)
fatal ("Can't mmap(%s): errno %d\n", old_name, errno);
#ifdef DEBUG
fprintf (stderr, "mmap(%s, %x) -> %x\n", old_name, stat_buf.st_size,
old_base);
#endif
old_file_h = (Elf32_Ehdr *) old_base;
old_program_h = (Elf32_Phdr *) ((byte *) old_base + old_file_h->e_phoff);
old_section_h = (Elf32_Shdr *) ((byte *) old_base + old_file_h->e_shoff);
old_section_names = (char *) old_base
+ OLD_SECTION_H(old_file_h->e_shstrndx).sh_offset;
for (old_sbss_index = 1; old_sbss_index < old_file_h->e_shnum;
old_sbss_index++)
{
#ifdef DEBUG
fprintf (stderr, "Looking for .sbss - found %s\n",
old_section_names + OLD_SECTION_H(old_sbss_index).sh_name);
#endif
if (!strcmp (old_section_names + OLD_SECTION_H(old_sbss_index).sh_name,
".sbss"))
break;
}
if (old_sbss_index == old_file_h->e_shnum)
fatal ("Can't find .sbss in %s.\n", old_name, 0);
if (!strcmp(old_section_names + OLD_SECTION_H(old_sbss_index - 1).sh_name,
".sdata"))
{
old_sdata_index = old_sbss_index - 1;
}
for (old_bss_index = 1; old_bss_index < old_file_h->e_shnum; old_bss_index++)
{
#ifdef DEBUG
fprintf (stderr, "Looking for .bss - found %s\n",
old_section_names + OLD_SECTION_H(old_bss_index).sh_name);
#endif
if (!strcmp (old_section_names + OLD_SECTION_H(old_bss_index).sh_name,
".bss"))
break;
}
if (old_bss_index == old_file_h->e_shnum)
fatal ("Can't find .bss in %s.\n", old_name, 0);
if (old_sbss_index != (old_bss_index - 1))
fatal (".sbss should come immediately before .bss in %s.\n", old_name, 0);
for (old_rel_dyn_index = 1; old_rel_dyn_index < old_file_h->e_shnum;
old_rel_dyn_index++)
{
#ifdef DEBUG
fprintf (stderr, "Looking for .rel.dyn - found %s\n",
old_section_names + OLD_SECTION_H(old_rel_dyn_index).sh_name);
#endif
if (!strcmp (old_section_names + OLD_SECTION_H(old_rel_dyn_index).sh_name,
".rel.dyn"))
break;
}
if (old_rel_dyn_index == old_file_h->e_shnum)
fatal ("Can't find .rel_dyn in %s.\n", old_name, 0);
old_rel_dyn_addr = OLD_SECTION_H(old_rel_dyn_index).sh_addr;
old_rel_dyn_size = OLD_SECTION_H(old_rel_dyn_index).sh_size;
old_bss_addr = OLD_SECTION_H(old_bss_index).sh_addr;
old_bss_size = OLD_SECTION_H(old_bss_index).sh_size;
old_sbss_addr = OLD_SECTION_H(old_sbss_index).sh_addr;
old_sbss_size = OLD_SECTION_H(old_sbss_index).sh_size;
if (old_sdata_index)
{
old_sdata_size = OLD_SECTION_H(old_sdata_index).sh_size;
}
#if defined(emacs) || !defined(DEBUG)
bss_end = (unsigned int) sbrk (0);
new_bss_addr = (Elf32_Addr) bss_end;
#else
new_bss_addr = old_bss_addr + old_bss_size + 0x1234;
#endif
if (old_sdata_index)
{
new_sdata_size = OLD_SECTION_H(old_sbss_index).sh_offset -
OLD_SECTION_H(old_sdata_index).sh_offset + old_sbss_size;
}
new_data3_addr = old_sbss_addr;
new_data3_size = old_sbss_size;
new_data3_offset = OLD_SECTION_H(old_sbss_index).sh_offset;
new_data2_addr = old_bss_addr;
new_data2_size = new_bss_addr - old_bss_addr;
new_data2_align = (new_data3_offset + old_sbss_size) %
OLD_SECTION_H(old_bss_index).sh_addralign;
new_data2_align = new_data2_align ?
OLD_SECTION_H(old_bss_index).sh_addralign - new_data2_align :
0;
new_data2_offset = new_data3_offset + old_sbss_size + new_data2_align;
old_bss_padding = OLD_SECTION_H(old_bss_index).sh_offset -
OLD_SECTION_H(old_sbss_index).sh_offset;
#ifdef DEBUG
fprintf (stderr, "old_bss_index %d\n", old_bss_index);
fprintf (stderr, "old_bss_addr %x\n", old_bss_addr);
fprintf (stderr, "old_bss_size %x\n", old_bss_size);
fprintf (stderr, "new_bss_addr %x\n", new_bss_addr);
fprintf (stderr, "new_data2_addr %x\n", new_data2_addr);
fprintf (stderr, "new_data2_size %x\n", new_data2_size);
fprintf (stderr, "new_data2_offset %x\n", new_data2_offset);
fprintf (stderr, "old_sbss_index %d\n", old_sbss_index);
fprintf (stderr, "old_sbss_addr %x\n", old_sbss_addr);
fprintf (stderr, "old_sbss_size %x\n", old_sbss_size);
fprintf (stderr, "old_rel_dyn_addr %x\n", old_rel_dyn_addr);
fprintf (stderr, "old_rel_dyn_size %x\n", old_rel_dyn_size);
if (old_sdata_index)
{
fprintf (stderr, "old_sdata_size %x\n", old_sdata_size);
fprintf (stderr, "new_sdata_size %x\n", new_sdata_size);
}
else
{
fprintf (stderr, "new_data3_addr %x\n", new_data3_addr);
fprintf (stderr, "new_data3_size %x\n", new_data3_size);
fprintf (stderr, "new_data3_offset %x\n", new_data3_offset);
}
#endif
if ((unsigned) new_bss_addr < (unsigned) old_bss_addr + old_bss_size)
fatal (".bss shrank when undumping???\n", 0, 0);
new_file = open (new_name, O_RDWR | O_CREAT, 0666);
if (new_file < 0)
fatal ("Can't creat(%s): errno %d\n", new_name, errno);
new_file_size = stat_buf.st_size +
((1 + (old_sdata_index ? 0 : 1)) * old_file_h->e_shentsize) +
new_data2_size + new_data3_size + new_data2_align;
if (ftruncate (new_file, new_file_size))
fatal ("Can't ftruncate(%s): errno %d\n", new_name, errno);
new_base = mmap (0, new_file_size, PROT_READ | PROT_WRITE, MAP_SHARED,
new_file, 0);
if (new_base == (caddr_t) -1)
fatal ("Can't mmap(%s): errno %d\n", new_name, errno);
new_file_h = (Elf32_Ehdr *) new_base;
new_program_h = (Elf32_Phdr *) ((byte *) new_base + old_file_h->e_phoff);
new_section_h = (Elf32_Shdr *) ((byte *) new_base +
old_file_h->e_shoff +
new_data2_size +
new_data2_align +
new_data3_size);
memcpy (new_file_h, old_file_h, old_file_h->e_ehsize);
memcpy (new_program_h, old_program_h,
old_file_h->e_phnum * old_file_h->e_phentsize);
PATCH_INDEX (new_file_h->e_shstrndx);
new_file_h->e_shoff += new_data2_size + new_data2_align + new_data3_size;
new_file_h->e_shnum += 1 + (old_sdata_index ? 0 : 1);
#ifdef DEBUG
fprintf (stderr, "Old section offset %x\n", old_file_h->e_shoff);
fprintf (stderr, "Old section count %d\n", old_file_h->e_shnum);
fprintf (stderr, "New section offset %x\n", new_file_h->e_shoff);
fprintf (stderr, "New section count %d\n", new_file_h->e_shnum);
#endif
for (n = new_file_h->e_phnum - 1; n >= 0; n--)
{
int 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 ((OLD_SECTION_H (old_sbss_index)).sh_addralign > alignment)
alignment = OLD_SECTION_H (old_sbss_index).sh_addralign;
#if 0
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
if (NEW_PROGRAM_H(n).p_type == PT_LOAD
&& (round_up ((NEW_PROGRAM_H (n)).p_vaddr
+ (NEW_PROGRAM_H (n)).p_filesz,
alignment)
== round_up (old_bss_addr, alignment)))
break;
}
if (n < 0)
fatal ("Couldn't find segment next to .bss in %s\n", old_name, 0);
NEW_PROGRAM_H(n).p_filesz += new_data2_size + new_data2_align +
new_data3_size;
NEW_PROGRAM_H(n).p_memsz = NEW_PROGRAM_H(n).p_filesz;
#if 1
for (n = new_file_h->e_phnum - 1; n >= 0; n--)
{
if (NEW_PROGRAM_H(n).p_vaddr
&& NEW_PROGRAM_H(n).p_vaddr >= new_data3_addr)
NEW_PROGRAM_H(n).p_vaddr += new_data2_size - old_bss_size +
new_data3_size - old_sbss_size;
if (NEW_PROGRAM_H(n).p_offset >= new_data3_offset)
NEW_PROGRAM_H(n).p_offset += new_data2_size + new_data2_align +
new_data3_size;
}
#endif
for (old_data_index = 1; old_data_index < old_file_h->e_shnum;
old_data_index++)
if (!strcmp (old_section_names + OLD_SECTION_H(old_data_index).sh_name,
".data"))
break;
if (old_data_index == old_file_h->e_shnum)
fatal ("Can't find .data in %s.\n", old_name, 0);
for (n = 1, nn = 1; n < old_file_h->e_shnum; n++, nn++)
{
caddr_t src;
if (n == old_sbss_index)
{
if (!old_sdata_index)
{
memcpy (&NEW_SECTION_H(nn), &OLD_SECTION_H(old_data_index),
new_file_h->e_shentsize);
NEW_SECTION_H(nn).sh_addr = new_data3_addr;
NEW_SECTION_H(nn).sh_offset = new_data3_offset;
NEW_SECTION_H(nn).sh_size = new_data3_size;
NEW_SECTION_H(nn).sh_flags = OLD_SECTION_H(n).sh_flags;
NEW_SECTION_H(nn).sh_addralign = OLD_SECTION_H(n).sh_addralign;
memcpy (NEW_SECTION_H(nn).sh_offset + new_base,
(caddr_t) OLD_SECTION_H(n).sh_addr,
new_data3_size);
nn += 2;
}
else
{
memcpy (new_data3_offset + new_base,
(caddr_t) OLD_SECTION_H(n).sh_addr,
new_data3_size);
nn ++;
}
}
else if (n == old_bss_index)
{
Elf32_Word tmp_align;
Elf32_Addr tmp_addr;
tmp_align = OLD_SECTION_H(n).sh_addralign;
tmp_addr = OLD_SECTION_H(n).sh_addr;
nn -= 2;
memcpy (&NEW_SECTION_H(nn), &OLD_SECTION_H(old_data_index),
new_file_h->e_shentsize);
NEW_SECTION_H(nn).sh_addr = new_data2_addr;
NEW_SECTION_H(nn).sh_offset = new_data2_offset;
NEW_SECTION_H(nn).sh_size = new_data2_size;
NEW_SECTION_H(nn).sh_addralign = tmp_align;
memcpy (NEW_SECTION_H(nn).sh_offset + new_base,
(caddr_t) tmp_addr, new_data2_size);
nn += 2;
}
memcpy (&NEW_SECTION_H(nn), &OLD_SECTION_H(n),
old_file_h->e_shentsize);
if (old_sdata_index && n == old_sdata_index)
NEW_SECTION_H(nn).sh_size = new_sdata_size;
if (n == old_sbss_index)
{
NEW_SECTION_H(nn).sh_offset += new_data2_size + new_data2_align +
new_data3_size;
NEW_SECTION_H(nn).sh_addr += new_data2_size + new_data2_align +
new_data3_size;
NEW_SECTION_H(nn).sh_addralign =
OLD_SECTION_H(nn + (old_sdata_index ? 1 : 0)).sh_addralign;
NEW_SECTION_H(nn).sh_size = 0;
}
else if (n == old_bss_index)
{
NEW_SECTION_H(nn).sh_offset += new_data2_size + new_data2_align +
new_data3_size - old_bss_padding;
NEW_SECTION_H(nn).sh_addr += new_data2_size;
NEW_SECTION_H(nn).sh_addralign =
OLD_SECTION_H((nn - (old_sdata_index ? 0 : 1))).sh_addralign;
NEW_SECTION_H(nn).sh_size = 0;
}
else if (NEW_SECTION_H(nn).sh_offset >= new_data3_offset)
NEW_SECTION_H(nn).sh_offset += new_data2_size +
new_data2_align +
new_data3_size -
old_bss_padding;
PATCH_INDEX(NEW_SECTION_H(nn).sh_link);
PATCH_INDEX(NEW_SECTION_H(nn).sh_info);
if (NEW_SECTION_H(nn).sh_type == SHT_NULL
|| NEW_SECTION_H(nn).sh_type == SHT_NOBITS)
continue;
if (!strcmp (old_section_names + OLD_SECTION_H(n).sh_name, ".data") ||
!strcmp (old_section_names + OLD_SECTION_H(n).sh_name, ".data1") ||
(old_sdata_index && (n == old_sdata_index)))
src = (caddr_t) OLD_SECTION_H(n).sh_addr;
else
src = old_base + OLD_SECTION_H(n).sh_offset;
memcpy (NEW_SECTION_H(nn).sh_offset + new_base, src,
((n == old_sdata_index) ?
old_sdata_size :
NEW_SECTION_H(nn).sh_size));
if (NEW_SECTION_H(nn).sh_type == SHT_SYMTAB
|| NEW_SECTION_H(nn).sh_type == SHT_DYNSYM)
{
Elf32_Shdr *spt = &NEW_SECTION_H(nn);
unsigned int num = spt->sh_size / spt->sh_entsize;
Elf32_Sym * sym = (Elf32_Sym *) (NEW_SECTION_H(nn).sh_offset +
new_base);
for (; num--; sym++)
{
if ((sym->st_shndx == SHN_UNDEF)
|| (sym->st_shndx == SHN_ABS)
|| (sym->st_shndx == SHN_COMMON))
continue;
PATCH_INDEX(sym->st_shndx);
}
}
}
{
Elf32_Rel *rel_p;
unsigned int old_data_addr_start;
unsigned int old_data_addr_end;
unsigned int old_data_offset;
unsigned int new_data_offset;
int i;
rel_p = (Elf32_Rel *)OLD_SECTION_H(old_rel_dyn_index).sh_addr;
old_data_addr_start = OLD_SECTION_H(old_data_index).sh_addr;
old_data_addr_end = old_data_addr_start +
OLD_SECTION_H(old_data_index).sh_size;
old_data_offset = (int)OLD_SECTION_H(old_data_index).sh_offset +
(unsigned int)old_base;
new_data_offset = (int)NEW_SECTION_H(old_data_index).sh_offset +
(unsigned int)new_base;
#ifdef DEBUG
fprintf(stderr, "old_data.sh_addr= 0x%08x ... 0x%08x\n", old_data_addr_start,
old_data_addr_end);
#endif
for (i = 0; i < old_rel_dyn_size/sizeof(Elf32_Rel); i++)
{
#ifdef DEBUG
fprintf(stderr, ".rel.dyn offset= 0x%08x type= %d sym= %d\n",
rel_p->r_offset, ELF32_R_TYPE(rel_p->r_info), ELF32_R_SYM(rel_p->r_info));
#endif
if (rel_p->r_offset)
{
unsigned int offset;
assert(old_data_addr_start <= rel_p->r_offset &&
rel_p->r_offset <= old_data_addr_end);
offset = rel_p->r_offset - old_data_addr_start;
#ifdef DEBUG
fprintf(stderr, "r_offset= 0x%08x *r_offset= 0x%08x\n",
rel_p->r_offset, *((int *)(rel_p->r_offset)));
fprintf(stderr, "old = 0x%08x *old =0x%08x\n",
(old_data_offset + offset - (unsigned int)old_base),
*((int *)(old_data_offset + offset)));
fprintf(stderr, "new = 0x%08x *new =0x%08x\n",
(new_data_offset + offset - (unsigned int)new_base),
*((int *)(new_data_offset + offset)));
#endif
*((int *)(new_data_offset + offset)) = *((int *)(old_data_offset + offset));
}
rel_p++;
}
}
if (close (old_file))
fatal ("Can't close(%s): errno %d\n", old_name, errno);
if (close (new_file))
fatal ("Can't close(%s): errno %d\n", new_name, errno);
if (stat (new_name, &stat_buf) == -1)
fatal ("Can't stat(%s): errno %d\n", new_name, errno);
n = umask (777);
umask (n);
stat_buf.st_mode |= 0111 & ~n;
if (chmod (new_name, stat_buf.st_mode) == -1)
fatal ("Can't chmod(%s): errno %d\n", new_name, errno);
}