#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.1).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 1997-02-13 11:22 PST by <mch@ratatosk>.
# Source directory was `/home/mch/tmp/unstrip'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode       name
# ------ ---------- ------------------------------------------
#   2120 -rw------- README
#  15472 -rw------- unstrip.c
#    201 -rw------- Makefile
#   1344 -rw-r--r-- unstrip.1
#
touch -am 1231235999 $$.touch >/dev/null 2>&1
if test ! -f 1231235999 && test -f $$.touch; then
  shar_touch=touch
else
  shar_touch=:
  echo
  echo 'WARNING: not restoring timestamps.  Consider getting and'
  echo "installing GNU \`touch', distributed in GNU File Utilities..."
  echo
fi
rm -f 1231235999 $$.touch
#
# ============= README ==============
if test -f 'README' && test X"$1" != X"-c"; then
  echo 'x - skipping README (file already exists)'
else
  echo 'x - extracting README (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'README' &&
unstrip version 1.0.1
X
Unstrip is a program which can often recover some symbolic information
from a stripped dynamically linked Solaris 2.4 SPARC executable and
make it available to common Solaris debuggers (gdb, adb, dbx). It can
only recover the symbolic information in the dynamic symbol table, but
it can usually recover enough to be useful.
X
It turns out that it is relatively simple to do this. The program makes
copies of the dynamic string table and the dynamic symbol table, and
adds these copies to the section header table.  It also takes care of
some additional complications when the section header string table is
missing or doesn't contain .symtab and .strtab.
X
Unstrip is intended to aid in reverse engineering efforts. For example
it can be used to help figure out what is going on when a stripped
binary dumps core. Often the name of the routine is helpful in trying
to trace the problem.
X
Please report bugs to mch@squirrel.com
X
A similar program by P.Kranenburg <pk@cs.few.eur.nl> is available for 
Sun OS 4.1.x from http://www.squirrel.com/squirrel/sun-stuff.html
OR
ftp://ftp.netcom.com/pub/he/henderso/unstrip.shar.gz
ftp://ftp.mindlink.net/pub/crypto/netcom-mirror/unstrip.shar.gz
X
Please note that some earlier versions of gdb and binutils have
problems dealing with the output of unstrip. I have tested the output
of unstrip against:
gdb 4.14 
binutils 2.5.2
/opt/SUNWspro/bin/dbx
adb supplied with Solaris 2.4
nm supplied with Solaris 2.4
X
To compile:
use either gcc or /opt/SUNWspro/bin/cc
simply:
X
gcc -o unstrip unstrip.c
OR
cc -o unstrip unstrip.c
X
Note that using unstrip with gdb 4.14 is pretty pointless, because gdb
is already smart enough to look at the dynamic symbol table in a stripped
dynamically linked binary.
X
Changes from 1.0-1.01
cosmetic change to unstrip.c
added man page
X
Changes
from version 0.21
- code added to deal with case where hdr.e_shstrndx = SHN_UNDEF, i.e.
X  the case where there is no section header string table
- the dynamic string table is now identified in the right way, 
X  i.e. by following the sh_link from the dynamic symbol table 
X  (type ST_DYNSYM)
SHAR_EOF
  $shar_touch -am 0213112197 'README' &&
  chmod 0600 'README' ||
  echo 'restore of README failed'
  shar_count="`wc -c < 'README'`"
  test 2120 -eq "$shar_count" ||
    echo "README: original size 2120, current size $shar_count"
fi
# ============= unstrip.c ==============
if test -f 'unstrip.c' && test X"$1" != X"-c"; then
  echo 'x - skipping unstrip.c (file already exists)'
else
  echo 'x - extracting unstrip.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'unstrip.c' &&
/* 
X * unstrip version 1.0.1
X * Author: Mark C. Henderson <mch@squirrel.com>
X * Placed in the public domain by the author. 2 September 1995.
X *
X * A quick and dirty hack to "unstrip" a Solaris 2.4 ELF SPARC binary
X * This program recovers some symbolic information from the dynamic
X * symbol table of the binary, and creates a new symbol table section
X * with that data.
X *
X * to compile: gcc -o unstrip unstrip.c  (or use /opt/SUNWspro/bin/cc)
X *
X * This can often be useful when reverse engineering dynamically linked
X * binaries, e.g. to find out what is going on when they dump core
X *
X * inspired by unstrip for Sun OS 4.1.x by P.Kranenburg <pk@cs.few.eur.nl>
X *
X * USE AT YOUR OWN RISK
X *
X * BUGS: Possibly several. Please report them to mch@squirrel.com.
X *
X * N.B. If this program malfunctions it can corrupt the binary you run
X *      it on. Please make backup copies.
X *
X * tested with adb, /opt/SUNWspro/bin/dbx, gdb 4.14, binutils 2.5.2
X * there are some problems with some earlier versions of gdb and binutils
X * 
X * Also note that there is no point in using this with gdb 4.14 as gdb
X * is smart enough to look at the dynamic symbol table (.dynsym) if the
X * binary is stripped
X */
X
static char header[]=
"$Header: /home/mch/src/un/unstrip.c,v 1.8 1996/04/26 04:21:36 mch Exp $";
X
#include <elf.h>
#include <sys/elf_SPARC.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
X
#define MAXSECTIONS 128
X
Elf32_Shdr sht[MAXSECTIONS + 1];
unsigned char   *secnames[MAXSECTIONS + 1];
unsigned char   *symtable;
unsigned char   *stringtable;
unsigned char   *shstringtable = NULL;
unsigned char   *new_shstringtable;
unsigned char   s[] = "\0.strtab\0.symtab\0\0\0\0\0\0\0\0\0\0";
unsigned char   s1[] = "\0.strtab\0.symtab\0.shstrtab\0.dynstr\0.dynsym\0.dynamic\0\0";
/* indices in s1
X *  .strtab         1
X *  .symtab         9
X *  .shstrtab       17
X *  .dynstr         27
X *  .dynsym         35
X *  .dynamic        43
X */
X
main(argc, argv)
int argc;
char    *argv[];
{
X    Elf32_Ehdr hdr, newhdr;
X    Elf32_Shdr newshdr, newshdr1, newshdr2;
X    int need_new_shstringtable = 0;
X    unsigned int    i;
X    int pad = 0, pad1 = 0, pad0 = 0, pad2 = 0;
X    int sto = (-1), sto1 = (-1);
X    int c;
X    FILE * chan;
X    int symflag = 0;
X    size_t size;
X    int dyn = (-1);
X    int stringidx = (-1);
X
X    if (argc < 2) {
X        fprintf(stderr, "usage: %s file\n", argv[0]);
X        exit(1);
X    }
X    chan = fopen(argv[1], "r+");
X
X    if (!chan) {
X        fprintf(stderr, "cannot open %s\n", argv[1]);
X        exit(1);
X    }
X    if (fread(&hdr, sizeof(hdr), 1, chan) <= 0) {
X        fprintf(stderr, "not an ELF file\n");
X        exit(1);
X    }
X    if (    (hdr.e_ident[0] != ELFMAG0) || 
X        (hdr.e_ident[1] != ELFMAG1) || 
X        (hdr.e_ident[2] != ELFMAG2) || 
X        (hdr.e_ident[3] != ELFMAG3)     ) {
X        fprintf(stderr, "not an ELF file\n");
X        exit(1);
X    }
X    if (hdr.e_ident[4] != ELFCLASS32) {
X        fprintf(stderr, "not a 32 bit object\n");
X        exit(1);
X    }
X    if (hdr.e_type != ET_EXEC) {
X        fprintf(stderr, "not an executable file\n");
X        exit(1);
X    }
X    if (!hdr.e_shoff) {
X        fprintf(stderr, "file has no section header table\n");
X        exit(1);
X    }
X    if (hdr.e_shnum > MAXSECTIONS) {
X        fprintf(stderr, "%s has %d sections. maximum is %d\n", 
X            argv[1], hdr.e_shnum, MAXSECTIONS);
X        fprintf(stderr, 
X            "increase MAXSECTIONS in unstrip.c and recompile\n");
X        exit(1);
X    }
X    if (fseek(chan, hdr.e_shoff, SEEK_SET) < 0) {
X        fprintf(stderr, "fseek failed\n");
X        exit(1);
X    }
X    for (i = 0; i < hdr.e_shnum ; i++) {
X        fread(&sht[i], sizeof(Elf32_Shdr), 1, chan);
X        if (sht[i].sh_type == SHT_DYNSYM) {
X            dyn = i; 
X            if (sht[i].sh_link != SHN_UNDEF)
X                stringidx = sht[i].sh_link;
X        }
X        if (sht[i].sh_type == SHT_SYMTAB) 
X            symflag++;
X    }
X    if (symflag) {
X        fprintf(stderr, "file is not stripped - already has symbol table \n");
X        exit(1);
X    }
X    if (dyn < 0) {
X        fprintf(stderr, "file is not dynamically linked\n");
X        exit(1);
X    }
X
X    /* get section name string table */
X    if (hdr.e_shstrndx != SHN_UNDEF) {
X        if (fseek(chan, sht[hdr.e_shstrndx].sh_offset, SEEK_SET) < 0) {
X            perror("fseek failed (shstr)"); 
X            exit(1);
X        }
X        if (!(shstringtable = malloc(sht[hdr.e_shstrndx].sh_size))) {
X            fprintf(stderr, "malloc failed\n"); 
X            exit(1);
X        }
X        if (fread(shstringtable, 1, sht[hdr.e_shstrndx].sh_size, chan)
X             < sht[hdr.e_shstrndx].sh_size) {
X            perror("fread failed (str)"); 
X            exit(1);
X        }
X    } else
X        shstringtable = NULL;  /* no section name string table */
X
X    /* now get the section names themselves */
X    if (shstringtable) {
X        for (i = 0; i < hdr.e_shnum ; i++) {
X            int len = strlen(&shstringtable[sht[i].sh_name]);
X            secnames[i] = malloc(len + 1);
X            strncpy(secnames[i], &shstringtable[sht[i].sh_name], len + 1);
X        }
X    } 
X
X    /* get dynamic symbol table */
X    if (sht[dyn].sh_size) {
X        if (fseek(chan, sht[dyn].sh_offset, SEEK_SET) < 0) {
X            perror("fseek failed (dyn)"); 
X            exit(1);
X        }
X        if (!(symtable = malloc(sht[dyn].sh_size))) {
X            fprintf(stderr, "malloc failed\n"); 
X            exit(1);
X        }
X        if (fread(symtable, 1, sht[dyn].sh_size, chan) < sht[dyn].sh_size) {
X            perror("fread failed (dyn)"); 
X            exit(1);
X        }
X    } else {
X        fprintf(stderr, "dynamic symbol table has zero size\n");
X        exit(1);
X    }
X
X    /* get string table */
X    if ((stringidx >= 0) && sht[stringidx].sh_size) {
X        if (fseek(chan, sht[stringidx].sh_offset, SEEK_SET) < 0) {
X            perror("fseek failed (str)"); 
X            exit(1);
X        }
X        if (!(stringtable = malloc(sht[stringidx].sh_size))) {
X            fprintf(stderr, "malloc failed\n"); 
X            exit(1);
X        }
X        if (fread(stringtable, 1, sht[stringidx].sh_size, chan)
X             < sht[stringidx].sh_size) {
X            perror("fread failed (str)"); 
X            exit(1);
X        }
X    } else {
X        fprintf(stderr, "warning: string table has zero size\n");
X        stringidx = (-1);   /* just to be sure */
X        exit(1);
X    }
X
X    /* find offset to .symtab and .strtab in section name string table */
X    if (shstringtable) {
X        for (i = 0; i < sht[hdr.e_shstrndx].sh_size - 8; i++) {
X            if (!strncmp(&shstringtable[i], ".symtab", 8)) {
X                sto = i;
X                break;
X            }
X        }
X        for (i = 0; i < sht[hdr.e_shstrndx].sh_size - 8; i++) {
X            if (!strncmp(&shstringtable[i], ".strtab", 8)) {
X                sto1 = i;
X                break;
X            }
X        }
X    }
X    else {
X        /* no shstringtable - so we try to create one */
X
X        new_shstringtable = malloc(sizeof(s1) + 2);
X        memset(new_shstringtable,0,sizeof(s1) + 2);
X        memcpy(new_shstringtable,s1,sizeof(s1));
X        newshdr2.sh_name = 17;  /* locn of .shstrtab */
X        sto = 9;                /* locn of .symtab */ 
X        sto1 = 1;               /* locn of .strtab */
X        sht[dyn].sh_name = 35;  /* locn of .dynsym */
X        if (stringidx >= 0) 
X            sht[stringidx].sh_name = 27;  /* locn of .dynstr */
X        newshdr2.sh_type = SHT_STRTAB;
X        newshdr2.sh_flags = 0;
X        newshdr2.sh_size = sizeof(s1) + 2;
X        newshdr2.sh_link = 0;
X        newshdr2.sh_info = 0;
X        newshdr2.sh_addralign = 8;  /* just in case */
X        newshdr2.sh_entsize = 0;
X        fseek(chan, 0, SEEK_END);
X        newshdr2.sh_offset = ftell(chan);
X        if (newshdr2.sh_addralign > 1) {
X            pad0 = newshdr2.sh_addralign - (newshdr2.sh_offset
X                 % newshdr2.sh_addralign);
X            newshdr2.sh_offset += pad0;
X        } else
X            pad0 = 0;
X        for (i = 0; i < pad0; i++)
X            putc(0, chan);
X        if (ftell(chan) != newshdr2.sh_offset) {
X            fprintf(stderr, "internal error (new_shstringtable-0)\n");
X            exit(1);
X        }
X        if (fwrite(new_shstringtable, (unsigned int)newshdr2.sh_size, 
X            1, chan) < 1) {
X            perror("fwrite failed (new_shstringtable-0)");
X            exit(1);
X        }
X
X        /* fill in .dynamic */
X        for (i = 0; i < hdr.e_shnum; i++)
X            if (sht[i].sh_type == SHT_DYNAMIC)
X                sht[i].sh_name = 43;
X
X        hdr.e_shstrndx = hdr.e_shnum;
X        memcpy(&sht[hdr.e_shnum],&newshdr2,sizeof(Elf32_Shdr));
X        hdr.e_shnum++;
X    }
X
X    if (sto < 0 || sto1 < 0) {
X        /* This is annoying. Sometimes the sh_stringtable doesn't contain
X           one or both of .symtab or .strtab */
X        need_new_shstringtable = 1;
X        new_shstringtable = malloc(sht[hdr.e_shstrndx].sh_size + 24 );
X        if (!new_shstringtable) {
X            fprintf(stderr, "malloc failed\n"); 
X            exit(1);
X        }
X        memcpy(new_shstringtable, shstringtable, 
X            sht[hdr.e_shstrndx].sh_size);
X        for (i = 0; i < 19 ; i++)
X            *(new_shstringtable + sht[hdr.e_shstrndx].sh_size + i) = s[i];
X
X        memcpy(&newshdr2, &sht[hdr.e_shstrndx], sizeof(newshdr2));
X        newshdr2.sh_size += 19;
X        sto1 = sht[hdr.e_shstrndx].sh_size + 1;
X        sto = sht[hdr.e_shstrndx].sh_size + 9;
X        fseek(chan, 0, SEEK_END);
X        newshdr2.sh_offset = ftell(chan);
X        if (newshdr2.sh_addralign > 1) {
X            pad0 = newshdr2.sh_addralign - (newshdr2.sh_offset
X                 % newshdr2.sh_addralign);
X            newshdr2.sh_offset += pad0;
X        } else
X            pad0 = 0;
X        for (i = 0; i < pad0; i++)
X            putc(0, chan);
X        if (ftell(chan) != newshdr2.sh_offset) {
X            fprintf(stderr, "internal error (new_shstringtable)\n");
X            exit(1);
X        }
X        if (fwrite(new_shstringtable, (unsigned int)newshdr2.sh_size, 
X            1, chan) < 1) {
X            perror("fwrite failed (new_shstringtable)");
X            exit(1);
X        }
X    }
X
X    if (stringidx < 0) {
X
X        memcpy(&newhdr, &hdr, sizeof(newhdr));
X        memcpy(&newshdr, &sht[dyn], sizeof(newshdr));
X
X        /* go to end of file */
X        fseek(chan, 0, SEEK_END);
X        newhdr.e_shoff = ftell(chan);
X
X        /* get new offset for section header table */
X        if (newhdr.e_shoff % 8)
X            pad2 = 8 - (newhdr.e_shoff % 8);
X        else
X            pad2 = 0;
X        newhdr.e_shoff += pad2;
X        for (i = 0; i < pad2; i++)
X            putc(0, chan);
X
X        newhdr.e_shnum++;
X
X        /* write new section header table */
X        for (i = 0; i < hdr.e_shnum ; i++) {
X            if (need_new_shstringtable && i == hdr.e_shstrndx) {
X                if (fwrite(&newshdr2, sizeof(Elf32_Shdr), 1, chan) < 1) {
X                    perror("fwrite failed");
X                    exit(1);
X                }
X            } else {
X                if (fwrite(&sht[i], sizeof(Elf32_Shdr), 1, chan) < 1) {
X                    perror("fwrite failed");
X                    exit(1);
X                }
X            }
X        }
X        newshdr.sh_type = SHT_SYMTAB;
X        if (newshdr.sh_addralign > 1)
X            pad = newshdr.sh_addralign - ((newhdr.e_shoff + newhdr.e_shnum 
X                *newhdr.e_shentsize) % (newshdr.sh_addralign));
X        else
X            pad = 0;
X
X        newshdr.sh_offset = (newhdr.e_shoff + newhdr.e_shnum * 
X            newhdr.e_shentsize) + pad;
X        if (sto >= 0)
X            newshdr.sh_name = sto;
X        if (fwrite(&newshdr, sizeof(Elf32_Shdr), 1, chan) < 1) {
X            perror("fwrite failed (newshdr)");
X            exit(1);
X        }
X        for (i = 0; i < pad; i++)
X            putc(0, chan);
X        if (fwrite(symtable, newshdr.sh_size, 1, chan) < 1) {
X            perror("fwrite failed (symtable)");
X            exit(1);
X        }
X        rewind(chan);
X        if (fwrite(&newhdr, sizeof(newhdr), 1, chan) < 1) {
X            perror("fwrite failed (elfhdr)");
X            exit(1);
X        }
X    } else {
X        memcpy(&newhdr, &hdr, sizeof(newhdr));
X        memcpy(&newshdr, &sht[dyn], sizeof(newshdr));
X        memcpy(&newshdr1, &sht[stringidx], sizeof(newshdr1));
X
X        /* go to end of file */
X        fseek(chan, 0, SEEK_END);
X        newhdr.e_shoff = ftell(chan);
X
X        /* get new offset for section header table */
X        if (newhdr.e_shoff % 8)
X            pad2 = 8 - (newhdr.e_shoff % 8);
X        else
X            pad2 = 0;
X        newhdr.e_shoff += pad2;
X        for (i = 0; i < pad2; i++)
X            putc(0, chan);
X
X        newhdr.e_shnum += 2;  /* two new sections */
X
X        /* write new section header table */
X        for (i = 0; i < hdr.e_shnum ; i++) {
X            if (need_new_shstringtable && i == hdr.e_shstrndx) {
X                if (fwrite(&newshdr2, sizeof(Elf32_Shdr), 1, chan) < 1) {
X                    perror("fwrite failed");
X                    exit(1);
X                }
X            } else {
X                if (fwrite(&sht[i], sizeof(Elf32_Shdr), 1, chan) < 1) {
X                    perror("fwrite failed");
X                    exit(1);
X                }
X            }
X        }
X        newshdr.sh_type = SHT_SYMTAB;
X        newshdr.sh_flags = 0;
X        newshdr1.sh_type = SHT_STRTAB;  /* redundant */
X        newshdr.sh_link = newhdr.e_shnum - 1;
X        newshdr.sh_flags = 0;
X        newshdr1.sh_flags = 0;
X        if (sto >= 0)
X            newshdr.sh_name = sto;
X        if (sto1 >= 0)
X            newshdr1.sh_name = sto1;
X
X        newshdr.sh_offset = newhdr.e_shoff + newhdr.e_shnum 
X            *newhdr.e_shentsize;
X
X        if (newshdr.sh_addralign > 1) {
X            pad = newshdr.sh_addralign - (newshdr.sh_offset 
X                 % newshdr.sh_addralign);
X            newshdr.sh_offset += pad;
X        }
X        newshdr1.sh_offset = newshdr.sh_offset + newshdr.sh_size;
X        if (newshdr1.sh_addralign > 1) {
X            pad1 = newshdr1.sh_addralign - (newshdr1.sh_offset 
X                 % newshdr1.sh_addralign);
X            newshdr1.sh_offset += pad1;
X        }
X        if (fwrite(&newshdr, sizeof(Elf32_Shdr), 1, chan) < 1) {
X            perror("fwrite failed (newshdr)");
X            exit(1);
X        }
X        if (fwrite(&newshdr1, sizeof(Elf32_Shdr), 1, chan) < 1) {
X            perror("fwrite failed (newshdr1)");
X            exit(1);
X        }
X        for (i = 0; i < pad ; i++)
X            putc(0, chan);
X
X        if (ftell(chan) != newshdr.sh_offset) {
X            fprintf(stderr, "internal error (1)\n");
X            fprintf(stderr, "%08x != %08x\n", ftell(chan), newshdr.sh_offset);
X            exit(1);
X        }
X
X        if (fwrite(symtable, newshdr.sh_size, 1, chan) < 1) {
X            perror("fwrite failed (symtable)");
X            exit(1);
X        }
X        for (i = 0; i < pad1 ; i++)
X            putc(0, chan);
X
X        if (ftell(chan) != newshdr1.sh_offset) {
X            fprintf(stderr, "internal error (2)\n");
X            fprintf(stderr, "%08x != %08x\n", ftell(chan), newshdr1.sh_offset);
X            exit(1);
X        }
X        if (fwrite(stringtable, newshdr1.sh_size, 1, chan) < 1) {
X            perror("fwrite failed (symtable)");
X            exit(1);
X        }
X        rewind(chan);
X        if (fwrite(&newhdr, sizeof(newhdr), 1, chan) < 1) {
X            perror("fwrite failed (elfhdr)");
X            exit(1);
X        }
X    }
X    fclose(chan);
}
X
X
SHAR_EOF
  $shar_touch -am 0213112197 'unstrip.c' &&
  chmod 0600 'unstrip.c' ||
  echo 'restore of unstrip.c failed'
  shar_count="`wc -c < 'unstrip.c'`"
  test 15472 -eq "$shar_count" ||
    echo "unstrip.c: original size 15472, current size $shar_count"
fi
# ============= Makefile ==============
if test -f 'Makefile' && test X"$1" != X"-c"; then
  echo 'x - skipping Makefile (file already exists)'
else
  echo 'x - extracting Makefile (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
CC=gcc
#CC=/opt/SUNWspro/bin/cc
CFLAGS=-O
unstrip: unstrip.c
X	$(CC) $(CFLAGS) -o unstrip unstrip.c
X
unstrip.shar.01: unstrip.c README Makefile
X	shar -o unstrip.shar README unstrip.c Makefile unstrip.1
SHAR_EOF
  $shar_touch -am 0213112097 'Makefile' &&
  chmod 0600 'Makefile' ||
  echo 'restore of Makefile failed'
  shar_count="`wc -c < 'Makefile'`"
  test 201 -eq "$shar_count" ||
    echo "Makefile: original size 201, current size $shar_count"
fi
# ============= unstrip.1 ==============
if test -f 'unstrip.1' && test X"$1" != X"-c"; then
  echo 'x - skipping unstrip.1 (file already exists)'
else
  echo 'x - extracting unstrip.1 (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'unstrip.1' &&
X.TH unstrip 1 "25 April 1996"
X.SH NAME
unstrip \- restore some symbolic information in a stripped dynamically linked binary
X.SH SYNOPSIS
X.B unstrip
X.IR file
X.SH DESCRIPTION
The 
X.B unstrip
command recovers some symbolic information in a stripped dynamically linked
Solaris 2.4/2.5 ELF executable and makes it available to the common Solaris
debuggers. The information recovered is limited to the symbol table 
information in the dynamic symbol table of the executable.
X.SH OPERANDS
The following operand is supported:
X.TP 10
X.I file
A path name referring to an executable file.
X.SH "EXIT STATUS"
The following exit values are returned:
X.TP 10
X.B 0
Successful completion.
X.TP
X.B >0
An error occurred.
X.SH "SEE ALSO"
X.BR strip (1),
X.BR ld (1),
X.BR elf (3E)
X.SH "NOTES"
X.LP
Care should be taken to make sure that one has backup copies of files
before using
X.B unstrip. 
If the program malfunctions, it may corrupt the executable.
X.LP
X.B unstrip
should really allow more than one file argument on its command line.
X.LP
Using
X.B unstrip
with gdb 4.14 or greater is actually pretty pointless, because gdb is
already smart enough to look at the dynamic symbol table.
X.LP
Some versions of gdb and binutils have problems dealing with the
unstripped binaries. If you have problems, fetch the latest versions.
X.SH AUTHOR
Mark Henderson <mch@squirrel.com>
SHAR_EOF
  $shar_touch -am 0213112197 'unstrip.1' &&
  chmod 0644 'unstrip.1' ||
  echo 'restore of unstrip.1 failed'
  shar_count="`wc -c < 'unstrip.1'`"
  test 1344 -eq "$shar_count" ||
    echo "unstrip.1: original size 1344, current size $shar_count"
fi
exit 0
