#include "config.h"
#include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <libc.h>
#include <mach-o/nlist.h>
#include <mach/mach.h>
#ifndef NeXT
#include <mach/machine/vm_param.h>
#endif
#include <mach-o/ldsyms.h>
#include <mach-o/loader.h>
#include <mach-o/reloc.h>
extern int malloc_freezedry (void);
int malloc_cookie;
#define VERBOSE
#ifdef VERBOSE
#define SHOW_MCOPY_WRITES
#endif
typedef struct region_t
{
struct region_t *next;
vm_address_t address;
vm_size_t size;
vm_prot_t protection;
vm_prot_t max_protection;
unsigned long file_offset;
unsigned long file_size;
} region_t;
typedef struct section_list_t
{
struct section_list_t *next;
struct section section;
} section_list_t;
static void fatal_unexec (char *format, ...)
{
va_list ap;
va_start (ap, format);
fprintf (stderr, "unexec: ");
vfprintf (stderr, format, ap);
fprintf (stderr, "\n");
va_end (ap);
exit (1);
}
static void print_region (struct region_t *region)
{
printf ("0x%8lx - 0x%8lx, length: 0x%8lx, protection: %c%c%c, max_protection: %c%c%c\n",
region->address, region->address + region->size, region->size,
(region->protection & VM_PROT_READ) ? 'r' : '-',
(region->protection & VM_PROT_WRITE) ? 'w' : '-',
(region->protection & VM_PROT_EXECUTE) ? 'x' : '-',
(region->max_protection & VM_PROT_READ) ? 'r' : '-',
(region->max_protection & VM_PROT_WRITE) ? 'w' : '-',
(region->max_protection & VM_PROT_EXECUTE) ? 'x' : '-');
}
static void print_regions (struct region_t *regions)
{
while (regions != NULL)
{
print_region (regions);
regions = regions->next;
}
}
static void print_section (struct section *section)
{
printf ("0x%8lx - 0x%8lx, length: 0x%8lx, offset: 0x%8lx\n",
section->addr, section->addr + section->size, section->size, section->offset);
}
static void print_sections (section_list_t *sections)
{
while (sections != NULL)
{
print_section (&(sections->section));
sections = sections->next;
}
}
static section_list_t *create_new_section_list(struct section *section_pointer)
{
section_list_t *section_list;
section_list = malloc (sizeof (section_list_t));
section_list->next = NULL;
section_list->section = *section_pointer;
return section_list;
}
static void append_section_list(section_list_t **first_list, section_list_t *last_list)
{
section_list_t *current;
if (*first_list == NULL) {
*first_list = last_list;
return;
}
current = *first_list;
while (current->next != NULL)
current = current->next;
current->next = last_list;
}
static void free_section_list(section_list_t *section_list)
{
section_list_t *next;
while (section_list != NULL)
{
next = section_list->next;
free(section_list);
section_list = next;
}
}
static void add_sections_from_segment(section_list_t **all_sections, struct segment_command *segment)
{
struct section *section_pointer;
int index;
section_pointer = (struct section *)(segment + 1);
for (index = 0; index < segment->nsects; index++) {
append_section_list (all_sections, create_new_section_list (section_pointer));
section_pointer++;
}
}
static int section_with_address (section_list_t *sections, unsigned long address)
{
int current_section, found_section;
found_section = 0;
current_section = 1;
while (sections != NULL)
{
if (address >= sections->section.addr && address < sections->section.addr + sections->section.size) {
found_section = current_section;
break;
}
sections = sections->next;
current_section++;
}
return found_section;
}
static void mcopy (int ffd, int tfd,
unsigned long fpos, unsigned long tpos, unsigned long len, char *reason)
{
if ((ffd == -1) && (tfd == -1))
{
char *f, *t, *e;
if (fpos > tpos)
{
f = (char *)fpos;
t = (char *)tpos;
e = (char *)(fpos + len);
while (f < e) *t++ = *f++;
}
else if (tpos > fpos)
{
f = (char *)(fpos + len);
t = (char *)(tpos + len);
e = (char *)fpos;
while (f > e) *--t = *--f;
}
printf ("read: %10lu - %10lu, length: %10lu [from MEM] (%s)\n", tpos, tpos+len, len, reason);
}
else if (ffd == -1)
{
if (lseek (tfd, tpos, L_SET) < 0)
fatal_unexec ("cannot seek target");
if (write (tfd, (void *)fpos, len) != len)
fatal_unexec ("cannot write target");
#ifdef SHOW_MCOPY_WRITES
printf ("write: %10lu - %10lu, length: %10lu [from MEM] (%s)\n", tpos, tpos+len, len, reason);
#endif
}
else if (tfd == -1)
{
if (lseek (ffd, fpos, L_SET) < 0)
fatal_unexec ("cannot seek source");
if (read (ffd, (void *)tpos, len) != len)
fatal_unexec ("cannot read source");
printf ("read: %10lu - %10lu, length: %10lu [from DISK] (%s)\n", tpos, tpos+len, len, reason);
}
else
{
int bread;
char *buf = alloca (1 << 16);
#ifdef SHOW_MCOPY_WRITES
printf ("write: %10lu - %10lu, length: %10lu [from DISK] (%s)\n", tpos, tpos+len, len, reason);
#endif
if (lseek (ffd, fpos, L_SET) < 0)
fatal_unexec ("cannot seek source");
if (lseek (tfd, tpos, L_SET) < 0)
fatal_unexec ("cannot seek target");
while((len > 0) && (bread = read (ffd, buf, MIN(1 << 16, len))) > 0)
{
if (bread < 0)
fatal_unexec ("cannot read source");
if (write (tfd, buf, bread) != bread)
fatal_unexec ("cannot write target");
len -= bread;
}
}
}
static void unexec_doit(int infd,int outfd)
{
int i, j, l, header_position, output_position;
region_t *regions = NULL, *cregion, **pregions;
struct mach_header mach_header;
struct load_command *load_command, *original_load_commands;
struct segment_command *segment_pointer;
struct symtab_command *symtab_pointer;
struct section *section_pointer;
section_list_t *all_sections = NULL;
unsigned long delta = 0;
#if defined(NS_TARGET) || !defined(NeXT)
struct dysymtab_command *dysymtab;
unsigned long extreloff = 0;
unsigned long nextrel = 0;
unsigned long locreloff = 0;
unsigned long nlocrel = 0;
struct relocation_info reloc_info;
unsigned long fixed_reloc_count = 0;
#endif
struct segment_command new_data_segment;
section_list_t *original_sections, *new_sections, **sect_ptr, *section_item;
malloc_cookie = malloc_freezedry();
#ifdef VERBOSE
printf ("malloc_cookie: %lx\n", malloc_cookie);
#endif
{
vm_address_t address;
vm_size_t size;
mach_port_t object_name;
#ifdef DARWIN
task_t task = mach_task_self();
struct vm_region_basic_info info;
mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT;
#else
task_t task = task_self();
vm_prot_t protection, max_protection;
vm_inherit_t inheritance;
boolean_t shared;
vm_offset_t offset;
#endif
for (address = VM_MIN_ADDRESS, pregions = ®ions;
#ifdef DARWIN
vm_region(task, &address, &size, VM_REGION_BASIC_INFO,
(vm_region_info_t)&info, &info_count, &object_name) == KERN_SUCCESS;
#else
vm_region(task, &address, &size, &protection, &max_protection,
&inheritance, &shared, &object_name, &offset) == KERN_SUCCESS;
#endif
address += size)
{
(*pregions) = alloca (sizeof(region_t));
(*pregions)->address = address;
(*pregions)->size = size;
#ifdef DARWIN
(*pregions)->protection = info.protection;
(*pregions)->max_protection = info.max_protection;
#else
(*pregions)->protection = protection;
(*pregions)->max_protection = max_protection;
#endif
(*pregions)->file_offset = 0;
(*pregions)->file_size = 0;
(*pregions)->next = 0;
pregions = &((*pregions)->next);
#ifdef DARWIN
if (object_name != MACH_PORT_NULL)
mach_port_deallocate(mach_task_self(), object_name);
info_count = VM_REGION_BASIC_INFO_COUNT;
#endif
}
}
#ifdef VERBOSE
printf ("Regions.\n");
print_regions (regions);
printf ("----------------------------------------------------------------------\n");
#endif
for (cregion = regions; cregion; cregion = cregion->next)
{
while ((cregion->next) &&
(cregion->next->address == cregion->address + cregion->size) &&
(cregion->next->protection == cregion->protection) &&
(cregion->next->max_protection == cregion->max_protection))
{
cregion->size += cregion->next->size;
cregion->next = cregion->next->next;
}
}
#ifdef VERBOSE
printf ("Concatenated regions.\n");
print_regions (regions);
printf ("----------------------------------------------------------------------\n");
#endif
for (pregions = ®ions; *pregions != NULL;)
{
if ( !((*pregions)->protection & VM_PROT_WRITE)
|| ((*pregions)->address >= 0x3000000))
{
*pregions = (*pregions)->next;
}
else
{
pregions = &((*pregions)->next);
}
}
#ifdef VERBOSE
printf ("Skipped regions.\n");
print_regions (regions);
printf ("----------------------------------------------------------------------\n");
#endif
mcopy (infd, -1, 0, (unsigned long) &mach_header, sizeof(mach_header), "read original mach header");
original_load_commands = alloca (mach_header.sizeofcmds);
mcopy (infd, -1, sizeof(mach_header), (unsigned long) original_load_commands, mach_header.sizeofcmds,
"read original load comands");
for (pregions = ®ions; *pregions;)
{
for (load_command = original_load_commands, i = 0;
i < mach_header.ncmds;
i++, load_command = (struct load_command *)(((void *)load_command) + load_command->cmdsize))
{
unsigned long ob, oe;
segment_pointer = (struct segment_command *)load_command;
if (segment_pointer->cmd != LC_SEGMENT || (strcmp (segment_pointer->segname, SEG_DATA) == 0)) continue;
ob = MAX((*pregions)->address, segment_pointer->vmaddr);
oe = MIN((*pregions)->address + (*pregions)->size, segment_pointer->vmaddr + segment_pointer->vmsize);
if (ob >= oe) continue;
if (ob == (*pregions)->address)
{
if (oe == (*pregions)->address + (*pregions)->size)
{
goto skip_region;
}
else
{
(*pregions)->address = oe;
(*pregions)->size -= (oe - ob);
}
}
else
{
if (oe == (*pregions)->address + (*pregions)->size)
{
(*pregions)->size -= (oe - ob);
}
else
{
cregion = alloca (sizeof(*cregion));
cregion->address = oe;
cregion->size = ((*pregions)->address + (*pregions)->size) - oe;
cregion->protection = (*pregions)->protection;
cregion->max_protection = (*pregions)->max_protection;
cregion->file_offset = 0;
cregion->file_size = 0;
cregion->next = (*pregions)->next;
(*pregions)->size = ob - (*pregions)->address;
(*pregions)->next = cregion;
}
}
}
pregions = &((*pregions)->next);
continue;
skip_region:
*pregions = (*pregions)->next;
}
#ifdef VERBOSE
printf ("Munged regions (1).\n");
print_regions (regions);
printf ("----------------------------------------------------------------------\n");
#endif
for (load_command = original_load_commands, i = mach_header.ncmds, header_position = sizeof(mach_header), output_position = 0;
i > 0;
i--, load_command = (struct load_command *)(((void *)load_command) + load_command->cmdsize))
{
switch (load_command->cmd)
{
case LC_SEGMENT:
segment_pointer = (struct segment_command *)load_command;
if (strcmp (segment_pointer->segname, SEG_DATA) == 0)
{
#if 1
unsigned long current_address;
original_sections = NULL;
new_sections = NULL;
sect_ptr = &original_sections;
section_pointer = (struct section *)(segment_pointer + 1);
for (l = 0; l < segment_pointer->nsects; l++)
{
if (!strncmp (section_pointer->sectname, "__la_symbol_ptr", 16))
{
section_item = alloca (sizeof (section_list_t));
section_item->next = *sect_ptr;
section_item->section = *section_pointer;
*sect_ptr = section_item;
sect_ptr = &(section_item->next);
}
else if (!strncmp (section_pointer->sectname, "__nl_symbol_ptr", 16))
{
section_item = alloca (sizeof (section_list_t));
section_item->next = *sect_ptr;
section_item->section = *section_pointer;
*sect_ptr = section_item;
sect_ptr = &(section_item->next);
}
else if (!strncmp (section_pointer->sectname, "__dyld", 16))
{
section_item = alloca (sizeof (section_list_t));
section_item->next = *sect_ptr;
section_item->section = *section_pointer;
*sect_ptr = section_item;
sect_ptr = &(section_item->next);
}
section_pointer++;
}
cregion = regions;
new_data_segment.cmd = LC_SEGMENT;
strncpy (new_data_segment.segname, SEG_DATA, 16);
new_data_segment.vmaddr = cregion->address;
new_data_segment.vmsize = 0;
new_data_segment.fileoff = 0;
new_data_segment.filesize = 0;
new_data_segment.maxprot = cregion->max_protection;
new_data_segment.initprot = cregion->protection;
new_data_segment.flags = segment_pointer->flags;
new_data_segment.nsects = 0;
new_data_segment.cmdsize = sizeof (struct segment_command);
#ifdef VERBOSE
printf ("Original sections:\n");
print_sections (original_sections);
printf ("----------------------------------------------------------------------\n");
#endif
sect_ptr = &new_sections;
current_address = new_data_segment.vmaddr;
while (original_sections != NULL)
{
if (current_address < original_sections->section.addr)
{
section_item = alloca (sizeof (section_list_t));
section_item->next = *sect_ptr;
section_pointer = &(section_item->section);
strncpy (section_pointer->sectname, "__data", 16);
strncpy (section_pointer->segname, SEG_DATA, 16);
section_pointer->addr = current_address;
section_pointer->size = original_sections->section.addr - current_address;
section_pointer->offset = 0;
section_pointer->align = 2;
section_pointer->reloff = 0;
section_pointer->nreloc = 0;
section_pointer->flags = 0;
section_pointer->reserved1 = 0;
section_pointer->reserved2 = 0;
*sect_ptr = section_item;
sect_ptr = &(section_item->next);
current_address = original_sections->section.addr;
}
section_item = original_sections;
original_sections = original_sections->next;
section_item->next = *sect_ptr;
*sect_ptr = section_item;
sect_ptr = &(section_item->next);
current_address += section_item->section.size;
}
if (current_address < cregion->address + cregion->size)
{
section_item = alloca (sizeof (section_list_t));
section_item->next = *sect_ptr;
section_pointer = &(section_item->section);
strncpy (section_pointer->sectname, "__data", 16);
strncpy (section_pointer->segname, SEG_DATA, 16);
section_pointer->addr = current_address;
section_pointer->size = cregion->address + cregion->size - current_address;
section_pointer->offset = 0;
section_pointer->align = 2;
section_pointer->reloff = 0;
section_pointer->nreloc = 0;
section_pointer->flags = 0;
section_pointer->reserved1 = 0;
section_pointer->reserved2 = 0;
*sect_ptr = section_item;
sect_ptr = &(section_item->next);
}
#ifdef VERBOSE
printf ("New sections:\n");
print_sections (new_sections);
printf ("----------------------------------------------------------------------\n");
#endif
sect_ptr = &new_sections;
while (*sect_ptr != NULL)
{
section_pointer = &((*sect_ptr)->section);
if (new_data_segment.fileoff == 0)
new_data_segment.fileoff = output_position;
new_data_segment.vmsize += section_pointer->size;
new_data_segment.filesize += section_pointer->size;
new_data_segment.nsects++;
new_data_segment.cmdsize += sizeof (struct section);
printf ("section is '%s'\n", section_pointer->sectname);
if (section_pointer->offset == 0)
{
mcopy (-1, outfd, (unsigned long) section_pointer->addr, output_position, section_pointer->size,
"SEG_DATA: write section data from memory");
}
else
{
mcopy (infd, outfd, (unsigned long) section_pointer->offset, output_position, section_pointer->size,
"SEG_DATA: write section data from original file");
}
section_pointer->offset = output_position;
output_position += section_pointer->size;
sect_ptr = &((*sect_ptr)->next);
}
mcopy (-1, outfd, (unsigned long) &new_data_segment, header_position, sizeof (struct segment_command),
"SEG_DATA: write segment command");
header_position += sizeof (struct segment_command);
while (new_sections != NULL)
{
mcopy (-1, outfd, (unsigned long) &(new_sections->section), header_position, sizeof (struct section),
"SEG_DATA: write section command");
header_position += sizeof (struct section);
append_section_list (&all_sections, create_new_section_list (&(new_sections->section)));
new_sections = new_sections->next;
}
mach_header.ncmds++;
regions = regions->next;
#endif
#if 1
mach_header.ncmds--;
j = segment_pointer->cmdsize;
for (cregion = regions; cregion != NULL; cregion = cregion->next)
{
mcopy (-1, outfd, cregion->address, output_position, cregion->size,
"SEG_DATA: write remainder data");
segment_pointer->cmd = LC_SEGMENT;
segment_pointer->cmdsize = sizeof(*segment_pointer);
strncpy (segment_pointer->segname, SEG_DATA, sizeof(segment_pointer->segname));
segment_pointer->vmaddr = cregion->address;
segment_pointer->vmsize = cregion->size;
segment_pointer->filesize = cregion->size;
segment_pointer->maxprot = cregion->max_protection;
segment_pointer->initprot = cregion->protection;
segment_pointer->nsects = 0;
segment_pointer->flags = 0;
segment_pointer->fileoff = output_position;
output_position += segment_pointer->filesize;
mcopy (-1, outfd, (unsigned long)segment_pointer, header_position, segment_pointer->cmdsize,
"SEG_DATA: write segment command for remainder data");
header_position += segment_pointer->cmdsize;
mach_header.ncmds++;
cregion->file_offset = segment_pointer->fileoff;
cregion->file_size = segment_pointer->filesize;
}
segment_pointer->cmdsize = j;
#endif
}
else
{
printf ("segment is '%s':\n", segment_pointer->segname);
mcopy (infd, outfd, segment_pointer->fileoff, output_position, segment_pointer->filesize,
"SEG_OTHER: write segment data");
section_pointer = (struct section *) (((void *)segment_pointer)+sizeof(*segment_pointer));
for(j = 0; j < segment_pointer->nsects; j++)
{
if (section_pointer[j].offset != 0)
section_pointer[j].offset = (section_pointer[j].offset - segment_pointer->fileoff) + output_position;
if (section_pointer[j].reloff != 0)
section_pointer[j].reloff = (section_pointer[j].reloff - segment_pointer->fileoff) + output_position;
}
if (strcmp (segment_pointer->segname, SEG_LINKEDIT) == 0)
{
delta = output_position - segment_pointer->fileoff;
}
segment_pointer->fileoff = output_position;
output_position += segment_pointer->filesize;
mcopy (-1, outfd, (unsigned long)load_command, header_position, load_command->cmdsize,
"SEG_OTHER: write segment command and its sections");
header_position += load_command->cmdsize;
add_sections_from_segment(&all_sections, segment_pointer);
}
break;
case LC_SYMTAB:
{
struct nlist *symtab;
symtab_pointer = (struct symtab_command *)load_command;
symtab = malloc(symtab_pointer->nsyms * sizeof(struct nlist));
mcopy(infd, -1, symtab_pointer->symoff, (unsigned long)symtab, symtab_pointer->nsyms * sizeof(struct nlist),
"Read old symbol table into memory");
symtab_pointer->symoff += delta;
symtab_pointer->stroff += delta;
mcopy (-1, outfd, (unsigned long)load_command, header_position, load_command->cmdsize,
"write symtab command");
header_position += load_command->cmdsize;
printf ("LC_SYMTAB: symoff = %ld, nsyms = %ld, stroff = %ld, strsize = %ld\n",
symtab_pointer->symoff, symtab_pointer->nsyms, symtab_pointer->stroff, symtab_pointer->strsize);
if (all_sections != NULL)
{
int index;
struct nlist *nlist_pointer;
section_list_t *section;
int section_index;
int changed_symtabs;
printf ("All sections:\n");
print_sections (all_sections);
changed_symtabs = 0;
nlist_pointer = symtab;
for (index = 0; index < symtab_pointer->nsyms; index++) {
if ((nlist_pointer->n_type & N_TYPE) == N_SECT) {
section_index = section_with_address(all_sections, nlist_pointer->n_value);
#if 0
printf ("%5d: 0x%08lx 0x%02x 0x%02x (0x%02x) 0x%04x 0x%08lx\n",
index, nlist_pointer->n_un.n_strx, nlist_pointer->n_type & 0xff, nlist_pointer->n_sect & 0xff,
section_index,
nlist_pointer->n_desc & 0xffff, nlist_pointer->n_value);
#endif
if (nlist_pointer->n_sect != section_index) {
nlist_pointer->n_sect = section_index;
changed_symtabs++;
}
}
nlist_pointer++;
}
printf ("Adjusted n_sect for %d symbol table entries.\n", changed_symtabs);
mcopy(-1, outfd, (unsigned long)symtab, symtab_pointer->symoff, symtab_pointer->nsyms * sizeof(struct nlist),
"write updated symbol table");
free_section_list(all_sections);
}
free(symtab);
}
break;
#if defined(NS_TARGET) || !defined(NeXT)
case LC_DYSYMTAB:
dysymtab = (struct dysymtab_command *)load_command;
extreloff = dysymtab->extreloff;
nextrel = dysymtab->nextrel;
locreloff = dysymtab->locreloff;
nlocrel = dysymtab->nlocrel;
if (dysymtab->nindirectsyms > 0) {
dysymtab->indirectsymoff += delta;
}
if (nextrel > 0) {
dysymtab->extreloff += delta;
}
if (nlocrel > 0) {
dysymtab->locreloff += delta;
}
mcopy (-1, outfd, (unsigned long)load_command, header_position, load_command->cmdsize,
"write dysymtab command");
header_position += load_command->cmdsize;
break;
#endif
default:
mcopy (-1, outfd, (unsigned long)load_command, header_position, load_command->cmdsize,
"write other load command");
header_position += load_command->cmdsize;
}
}
mach_header.sizeofcmds = header_position - sizeof(mach_header);
mcopy (-1, outfd, (unsigned long) &mach_header, 0, sizeof(mach_header), "write mach header");
#if defined(NS_TARGET) || !defined(NeXT)
if (mach_header.flags & MH_PREBOUND) {
return;
}
if (lseek (infd, locreloff, L_SET) < 0)
fatal_unexec ("cannot seek input file");
fixed_reloc_count = 0;
for (i = 0; i < nlocrel; i++)
{
long zeroval = 0;
struct scattered_relocation_info *si;
if (read (infd, &reloc_info, sizeof(reloc_info)) != sizeof(reloc_info))
fatal_unexec ("cannot read input file");
#if 1
#ifdef VERBOSE
printf ("%2d: reloc: %lx, start: %lx, end: %lx\n", i, reloc_info.r_address,
new_data_segment.vmaddr, new_data_segment.vmaddr + new_data_segment.filesize);
#endif
if (reloc_info.r_address >= new_data_segment.vmaddr
&& reloc_info.r_address < new_data_segment.vmaddr + new_data_segment.filesize)
{
fixed_reloc_count++;
mcopy (-1, outfd, (unsigned long) &zeroval,
new_data_segment.fileoff + reloc_info.r_address - new_data_segment.vmaddr,
1 << reloc_info.r_length, "fix local relocation entry");
}
#endif
}
printf ("Fixed %lu/%lu local relocation entries in data segment(s).\n", fixed_reloc_count, nlocrel);
if (lseek (infd, extreloff, L_SET) < 0)
fatal_unexec ("cannot seek input file");
for (i = 0; i < nextrel; i++)
{
long zeroval = 0;
if (read (infd, &reloc_info, sizeof(reloc_info)) != sizeof(reloc_info))
fatal_unexec ("cannot read input file");
#if 1
#ifdef VERBOSE
printf ("%2d: reloc: %lx, start: %lx, end: %lx\n", i, reloc_info.r_address,
new_data_segment.vmaddr, new_data_segment.vmaddr + new_data_segment.filesize);
#endif
if (reloc_info.r_address >= new_data_segment.vmaddr
&& reloc_info.r_address < new_data_segment.vmaddr + new_data_segment.filesize)
{
fixed_reloc_count++;
mcopy (-1, outfd, (unsigned long) &zeroval,
new_data_segment.fileoff + reloc_info.r_address - new_data_segment.vmaddr,
1 << reloc_info.r_length, "fix external relocation entry");
}
#endif
}
printf ("Fixed %lu/%lu external relocation entries in data segment(s).\n", fixed_reloc_count, nextrel);
#endif
}
void unexec (char *outfile, char *infile)
{
char tmpfile[MAXPATHLEN + 1];
int infd, outfd;
if ((infd = open (infile, O_RDONLY, 0)) < 0)
fatal_unexec ("cannot open input file `%s'", infile);
strcpy (tmpfile, outfile);
strcat (tmpfile, "-temp");
if ((outfd = open (tmpfile, O_RDWR|O_TRUNC|O_CREAT, 0755)) < 0)
fatal_unexec ("cannot open temporary output file `%s'", tmpfile);
unexec_doit (infd, outfd);
close (infd);
close (outfd);
if (rename (tmpfile, outfile) < 0)
{
unlink (tmpfile);
fatal_unexec ("cannot rename `%s' to `%s'", tmpfile, outfile);
}
}