💾 Archived View for gemini.spam.works › mirrors › textfiles › hacking › license.asc captured on 2023-01-29 at 07:50:43.

View Raw

More Information

⬅️ Previous capture (2021-12-04)

-=-=-=-=-=-=-

-----BEGIN PGP SIGNED MESSAGE-----

Guerilla Guide to Licensing Software

DRAFT VERSION 0.1

Abstract

This short article discusses the weaknesses of various licensing schemes
to control the use and distribution of Unix software. In particular,
examples will be given of actual commercial products which can be
"cracked" with a minimum of effort. The intention is to provide
some observations which will be of use to implementers and designers
of software or those integrating licence managers into their products.



DISCLAIMER: I am not recommending that you steal software. The purpose
of this article is to point out to vendors to weaknesses of the schemes
they use. Please do not use this information for illegal purposes.

For obvious reasons the author of this document wishes to remain anonymous.

Network licence servers or node locked licences have become very
popular in the Unix world over the last few years. Most vendors
now use them, as they provide an easy means to control the use
and distribution of commerical software with a minimum of customer
inconvenience. Unfortunately many of these provide a false sense
of security for the vendor in that any skilled "cracker" can break
the scheme thus turning a demonstration version into an unauthorised 
unlimited licence version or adding licences to an already purchased
version.

In particular, many vendors provide demonstration versions of software
which either have expiry dates (fully functional short term licences)
or have limited functionality. This approach is inherently weak in
that it is usually possible to convert this demonstration version
into a fully functional long term version. Vendors like this sort
of scheme because a customer can call up and purchase the product,
and have a licensed version immediately, if he/she has a demonstration
version, say from a CD-ROM library of demonstration programs. However,
in this case, the demonstration version will undoubtedly be distributed
to a lot of potential customers. From the vendor's point of view,
the sort of "cracking" which turns a demonstration version into a "real"
version is probably a more serious problem that "cracking" to gain
extra licences.

I'm going to start with a brief overview of the methods most of these
products use to generate licences. Most of the schemes use some
sort of cryptographically strong hash function to give a level
of security. Examples of these are MD2, MD4, MD5, Snefru and the
NIST Secure Hash Standard (FIPS-180). These hash functions all
have the property that it is computationally difficult to determine
the input given the output or to determine two outputs which hash to 
the same value.  The security of the system usually relies on the secrecy 
of the hash function used and the difficulty of inverting that function.

a) Node-locked licences. These are licenses to run a certain number
of copies of a product on a single computer. Usually some sort
of unique host identifier is hashed together with the number of
licences, expiry date, and possibly some customer identification information
(company name &c.). The host identifier and number of licences
are stored in plaintext together with the hash value.

e.g.
hostid 66777777
hostname foo
licences 7
key 4f4c998319b26020a20ed60e146c7d60

For our example the hash function is a function H of
hostid, hostname and licenses, probably constructed using the
composition of some simple string manipulation and either
one of the "secure" hash functions mentioned above or perhaps
a hash function designed by the licensing software vendor.

The vendor, given the hostid, hostname and licences, computes
H(hostid,hostname,licences) and gives this to the customer upon
purchase.  The product would check the hash value by computing
H(hostid,hostname,licenses) and checking that value against the licence
key. If they don't match, a violation is detected and presumably the
product will not function correctly. In our specific example if
an unscrupulous customer changes the value to "licences" to 8,
the key the product computes will not match the key value
given by the vendor and a violation will be detected.

Now, you might ask, "why is this system inherently weak?". Well, the
answer is sort of obvious, if our unscrupulous user could monitor
the execution of the program in order to find out what the key should be
given altered licensing data, then he/she could "crack" the system.
If a demonstration version has an expiry date, this sort of scheme
might allow someone to change this date and therefore have access
to a "real" version of the product.

b) Floating licences:
Are essentially very similar. A licence server runs on a host or
set of hosts. The verification of the key is usually done by the
server on startup. Some sort of protocol (which might include
encryption) is used for client-server communications.

The attacks I give below will involve actual modification
of the product in question. None of them took over four hours (and
I'm not a particularly talented hacker).

As an aside, an obvious solution to the above would be the use of public
key methods. There are two obvious problems here, the first being that
all such methods are patented and thus the user would have to pay a license
fee. The second is that digital signature algorithms often would produce
keys that are much too large for the average user to type in. Clearly
a 128 byte key (256 hex digits) would be acceptable to most customers, but
if you use something like RSA to sign your licensing information, you
are looking at something on that scale.

Types of attacks:
1. Bypass the licensing routines altogether.
2. Run the licensing routine but change its return value.
3. Determine what the licence key "should be" given the other (altered)
   licensing data.
4. Change the values of certain interesting variables.

The attacks I'll discuss will involve the use of some sort of debugger.
The appropriate debugger often depends on the system you are working with.
Usually lower level debuggers are better as little or no symbolic
information will be available (most vendors strip binaries). However,
in many cases if the product is dynamically linked, you can "unstrip" 
the relevant programs to restore much of the symbolic data. Obviously 
you are ahead if there is a routine called check_licence() and you know 
when and where it is called, you have some useful information to attack
the product. (an unstrip program for sun os 4.1 is included with this
file. The same sort of thing works under AIX 3.2 and Solaris 2.x, for 
example).

So for licence software designers and integrators.

I. First principle. Obscure symbolic information.
   - Strip your binaries. 
   - Statically link if possible. 
   - If you can't statically link, try and obscure the symbol names. One
     way to do this is to obfuscate all of the symbol names in your
     distribution version. (opqcp available in comp.sources.misc vol 13 is
     a useful tool for this, some might argue that using C++ is enough ;-) ).


The sample attacks will explain some obvious avenues of attack. The
product names will be changed to protect the vendors in question.

Example 1. Product A, Sun 4, SUN OS 4.1.1
Product A is a nifty tool. The vendor will send you a demo version
if you call and ask. The demo version is fully functional, but will
expire after a couple of weeks.

Step 1 is to unstrip (indeed this one is dynamically linked).

Step 2. Change the expiry date in the licence file from 31-Dec-1992 
to 31-Dec-1999 (now the key won't match what the vendor would have given 
us if he/she had given us that expiry date).

A little work with adb reveals the following:

product A forks before it checks the licensing information. The child
does all the work.

So, step 3, reverse the sense of the test after the fork.

The source code (which we don't have) looks something like:
if (fork()) {
    /* parent - just die */
    exit(0);
}
else {
    /* child - do the work */
}

So, we try and reverse the test. The resulting binary won't work properly
of course, but the point is to get some information on how things work.

So we:
cp product_a product_a_hacked_version
adb -w ./product_a_hacked_version
0x2664  - reverse the sense of this test.
i.e.
change
_main+0x30:     be,a    _main + 0x40
to
_main+0x30:     bne,a   _main + 0x40

e.g. you might do this by
0x2664?w 3280


It turns out (this is too easy), if you set a breakpoint on strncmp
after doing this, you will find that (on the first call to strncmp) one 
of the parameters contains the value of the key from the licence file and
one of them contains the value the key should have. 

One can then proceed by changing the key in the licence file. In this
case, I could change the expiry date to some date in 1999.


4> adb ./product_a_hacked_version
strncmp:b
:r 0 1 ./product_a_hacked_version
SIGCHLD 20: child status change
stopped at      _isatty+0x98:   bgeu    _isatty + 0xc0
:c 0
breakpoint      _strncmp:       save    %sp, -0x60, %sp
:s
stopped at      _strncmp+4:     call    __DYNAMIC + 0x60
:s
stopped at      _strncmp+8:     sethi   %hi(0xb000), %g0
$C
_strncmp(0x11330,0x157dc,0x14,0xf7fff314,0xf7fff314,0x63) + 8
_lm_checkout(0xe3f8,0x3ff00000,0x0,0x1,0x0,0xe3b0) + a60
_lm_checkout(0xe3f8,0x3ff00000,0x0,0x1,0x0,0xe3b0) + dc
_main(0x4,0x0,0x0,0x1,0x0,0x834) + 29c
0x11330?s
__mb_cur_max+0x1fc:             5AA6DC71361F95E20D1C

and yes, this is the required key value.

("too easy" you say, this product sells for over $2000 per licence!).

Indeed, the "unhacked" version runs nicely with the key and expiry
date in the new licence file. You might say that the vendor was
singularly stupid, but strangely enough, this sort of thing is
very common.


II. Second Principle.
    Fully functional date locked demonstration versions are dangerous things.

III. Third Principle.
    Lock a smart hacker who knows nothing about the internals of your
    product (new employees can be good for this) in a room with your product 
    for a day and see if he/she can crack it. Certainly this sort of 
    thing is standard practice in the design of cryptosystems.

IV. Fourth Principle.
    Try and obscure the functioning of your licensing code.



Example 2. The case of product B.
I'm going into somewhat less detail here because this is in a way similar
to product A. Product B is statically linked :-(
Product B is another nifty tool selling for >$1000 per license.

The vendor will give you a date locked fully functional version. 
If the licence key is not verified (expired or whatever), product
B comes up in demonstration mode which has restricted functionality.

The mode (demonstration or licenced) is controlled by a single boolean 
variable (integer value 0 or 1). This variable is checked when
someone tries one of the restricted functions, and the user
is either permitted to perform the function or not based on the
value of this variable.

The way to crack this, of course, was to insert an instruction near
the end of the licensing code which set the data location to the value 1.

Clues:
1. Whenever a restricted function was performed that address was accessed.
   It wasn't accessed otherwise after the initial startup.
2. Value of 0 or 1 was a dead giveaway. A pointer to a string might have
   been a better choice for the value of TRUE.



Example 3. The case of product C.

Product C is less expensive than either of the above. It was somewhat
harder to crack. Product C is also statically linked :-( We start
working from the case where there is an expired demonstration licence.


We use adb again. The following steps do the trick.

1. Locate main.
2. Find the call directly from main which prints out a message which
   indicates that the licence file is invalid.
3. proceed down the "tree" of calls to find the lowest level routine
   which prints this message. A little trial and error shows the conditional
   branch which determines if a key is valid in this routine. Note that
   this happens just after getimeofday is called!

            or      %o2, 0x57, %o2
            mov     0x5, %o0
            call    0x412c8
            nop
            ba      0x69e18
            clr     %i3
            ld      [%o5 + 0x37c], %o5
            andcc   %o5, 0x2, %g0
            be,a    0x69e24
            orcc    %g0, %i3, %g0
            call    0x13af50  - this calls gettimeofday(2)
            clr     %o0
            ld      [%i5 + 0x10], %o7
            cmp     %o7, %o0
0x69e08     bl,a    0x69e24     (originally bgeu,a 0x69e24)
            orcc    %g0, %i3, %g0
            mov     0x1, %l2
            clr     %i3
            clr     %i0

Now the expired demo license will serve as a perpetual demo licence.
The demo version is fully functional

Now the expired demo license will serve as a perpetual demo licence.
The demo version is fully functional

   


Example 4. The case of product D.

Product D is statically linked. 

We can bypass the licence routine completely by replacing the
call to it with a nop. The folling is an RS6000 exmaple.

0x10018444 (???) 4818e101         bl   0x101a6544 (???)
replace this with 38600000  lil r3,0

again we could determine the routine of interest by finding out
which routine printed the violation message when there was no
licence.

Another option in this case was to insert a return instruction just
after the call to the licence routine (which incidentally calls
gethostid first thing and later displays a message about not
being able to get a licence).

- From this we might observe, two related principles.

V. Fifth Principle. Modularity is a bad thing.
If someone can subvert your code by altering one routine or changing
one variable, then the code is vunerable. Software engineering practice
would seem to dictate that you should have one routine or small set of
routines where all of the licensing code resides. However, this sort
of approach is inherently insecure. For instance in the above the
routine which prints the message indicating a licensing violation was
the routine which verified the licensing data. It would have been much
better to seperate the two.

VI. Sixth Principle. When you write code for the licensing system, make 
sure that it can't be easily subverted by changing any one single 
instruction. Program defensively. Check often for licence violations. 
Duplicate all the "check" code if necessary, so that the subversion of 
a single routine will not sucessfully crack the system. If the cracker
has to simultaneously modify three or more instructions/variables, cracking
is significantly harder.



Certainly it is impossible to make your system unbreakable. Consider
in all of the above, a somewhat experienced hacker could crack the
system in less than four hours. The goal of the implementor should
be to design the licensing components in a sufficiently convoluted
way so that much more effort is needed.


Appendix.

Unstrip for Sun 4/sun os 4.1.1


From: pk@cs.few.eur.nl (Paul Kranenburg)
Newsgroups: alt.sources
Subject: unstrip a stripped dynamically linked executable.
Keywords: dynamic linking, debugging
Message-ID: <1992Jan22.161325.22872@cs.few.eur.nl>
Date: 22 Jan 92 16:13:25 GMT
Sender: news@cs.few.eur.nl
Reply-To: pk@cs.eur.nl
Organization: Erasmus University Rotterdam
Lines: 1319

Ever found the odd core file lying around in your root directory
and discovered that it was dropped by some system supplied daemon
which didn't even contained a name list?

If this happens on SunOS 4.x system (and it certainly does happen on
ours) and the executable is dynamically linked (as most of them are)
you may at least be able to get a decent traceback with adb(1)
after forcing the necessary symbolic information into the open
again.

This is the purpose of the enclosed programme, which takes advantage
of the fact that the run-time linker (ld.so) needs the same symbolic
information to perform its task and is therefore included in
the executable's text segment by its companion, ld(1).

Quote from README:

"Information for the run-time linker is stored in an executable's text
and data segment. This includes a symbol- and string table in standard
(a.out) format. Careful examination of the <link.h> header file and
tracing of some simple dynamically linked programs reveal the intrinsics
of the run-time link process, enabling the extraction of the symbol
table and putting it back on the spot where it used to be before
the executable was stripped."


Cheers, Paul.

- -------------------------------------------------------------------------
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#       "End of archive 1 (of 1)."
# Contents:  Makefile README defs.h nm.c nmd.1 unstrip.1 unstrip.c
#   util.c
# Wrapped by pk@kaa on Wed Jan 22 16:35:51 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(746 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#
X# @(#)Makefile 1.2 92/01/22
X#
XBINDIR=/usr/local/bin
XMANDIR=/usr/local/man/man1
XCC=cc
XCFLAGS=-O
XOBJS=util.o
XKIT=   Makefile README unstrip.1 nmd.1 defs.h util.c unstrip.c nm.c
X
Xall: nm unstrip
X
Xnm: nm.o $(OBJS)
X   $(CC) $(CFLAGS) -o $@ nm.o $(OBJS)
X
Xunstrip: unstrip.o $(OBJS)
X   $(CC) $(CFLAGS) -o $@ unstrip.o $(OBJS)
X
Xd2o: d2o.o $(OBJS)
X   $(CC) $(CFLAGS) -o $@ d2o.o $(OBJS)
X
Xinstall: nm unstrip
X   install -m 555 nm $(BINDIR)/nmd
X   install -m 555 unstrip $(BINDIR)
X
Xinstall.man: unstrip.1 nmd.1
X   install -m 444 unstrip.1 $(MANDIR)
X   install -m 444 nmd.1 $(MANDIR)
X
Xclean:
X   rm -f *.o a.out core nm unstrip d2o Part?? nm.tar.Z
X
Xkit: $(KIT)
X   makekit $(KIT)
X
Xtar: $(KIT)
X   tar cf - $(KIT) | compress > nm.tar.Z
X
Xnm.o: defs.h
Xunstrip.o: defs.h
Xd2o.o: defs.h
END_OF_FILE
if test 746 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(1634 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X@(#)README 1.2 92/01/22
X
Xunstrip - restore symbols and relocation bits in a dynamically
X     linked object file
X
Xnmd    - a nm(1) like programme that is capable of showing the name
X     list and relocation information of dynamically linked executables
X
X
XInformation for the run-time linker is stored in an executable's text
Xand data segment. This includes a symbol- and string table in standard
X(a.out) format. Careful examination of the <link.h> header file and
Xtracing of some simple dynamically linked programs reveal the intrinsics
Xof the run-time link process, enabling the extraction of the symbol
Xtable and putting it back on the spot where it used to be before
Xthe executable was stripped.
X
XFortunately, the "internal" symbol table still includes entries
Xof symbols that were already resolved by the static linking process,
Xprovided they carry the "external" attribute.
X
XThe mechanisms employed by the dynamic linker have been declared
Xvolatile by Sun (cf. link(5)), so these programs may stop working
Xat any moment (perhaps even without upgrading your OS :-).
X
XHowever, the SVR4 SPARC ABI specifies many items related to dynamic
Xlinking and also documents the relocation types which are nowhere to
Xbe found in SunOS 4.1.x documentation (most notably the RELOC_JMP_SLOT
Xtype that appears to define the relation of a symbol to the Procedure
XLinkage Table). If this extrapolates to SunOS 4.1.x, some hacks in
Xthese programs could be cleaned up.
X
XA makefile and two quick manual pages are included in this distribution.
X
X
XAuthor:
X
X   P.Kranenburg
X   Deptartment of Computer Science
X   Erasmus University Rotterdam (NL)
X   email: pk@cs.few.eur.nl
END_OF_FILE
if test 1634 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'defs.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'defs.h'\"
else
echo shar: Extracting \"'defs.h'\" \(764 characters\)
sed "s/^X//" >'defs.h' <<'END_OF_FILE'
X/*
X * @(#)defs.h  1.2 92/01/22
X */
X
Xstruct execmap {
X   struct exec *e_hdr;         /* exec header */
X   caddr_t     e_text;         /* text segment */
X   caddr_t     e_data;         /* data segment */
X   struct nlist    *e_nlist;       /* symbol table */
X   int     e_nnlist;       /* # of nlist */
X#ifdef sparc
X#define reloc_info reloc_info_sparc
X#endif
X#ifdef mc68000
X#define reloc_info reloc_info_68k
X#endif
X   struct reloc_info   *e_trel;    /* relocation tables */
X   struct reloc_info   *e_drel;
X   int     e_ntrel;        /* # of text relocations */
X   int     e_ndrel;        /* # of data relocations */
X   char        *e_sym;         /* string table */
X#ifdef sun
X   int     e_mlen;         /* size of mapped object */
X#endif
X};
X
Xstruct execmap *mapfile();
Xint unmapfile();
Xvoid prtsym(), prthdr(), prtrel();
Xint ncompare(), scompare(), rncompare(), rscompare();
END_OF_FILE
if test 764 -ne `wc -c <'defs.h'`; then
    echo shar: \"'defs.h'\" unpacked with wrong size!
fi
# end of 'defs.h'
fi
if test -f 'nm.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'nm.c'\"
else
echo shar: Extracting \"'nm.c'\" \(5881 characters\)
sed "s/^X//" >'nm.c' <<'END_OF_FILE'
X/*
X * @(#)nm.c    1.2 92/01/22    - display an a.out symbol table
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <a.out.h>
X#ifndef sun
X#include <nlist.h>
X#endif
X#ifdef sun
X#include <link.h>
X#endif
X#include "defs.h"
X
Xextern char *malloc();
X
Xstatic aflag, dflag, gflag, hflag, nflag, pflag, rflag, uflag, xflag;
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X   int c, status = 0;
X   extern int optind;
X   extern char *optarg;
X
X   while ((c = getopt(argc, argv, "adghnprux")) != EOF) {
X       switch (c) {
X       case 'a':
X           aflag++;
X           break;
X       case 'n':
X           nflag++;
X           break;
X       case 'p':
X           pflag++;
X           break;
X       case 'h':
X           hflag++;
X           break;
X       case 'r':
X           rflag++;
X           break;
X       case 'u':
X           uflag++;
X           break;
X       case 'g':
X           gflag++;
X           break;
X       case 'x':
X           xflag++;
X           break;
X#ifdef sun
X       case 'd':
X           dflag++;
X           break;
X#endif
X       default:
X           fprintf(stderr, "Usage: %s file ...\n", argv[0]);
X           return -1;
X           break;
X       }
X   }
X   if (argc <= optind) {
X       status = nm("a.out") == -1;
X   } else if (argc == optind + 1) {
X       status = nm(argv[optind]) == -1;
X   } else {
X       for (; optind < argc; optind++) {
X           printf("\n%s:\n", argv[optind]);
X           status |= nm(argv[optind]) == -1;
X       }
X   }
X   return status;
X}
X
Xnm(file)
Xchar *file;
X{
X   int fd;
X   struct exec *hp;
X   struct execmap *mp;
X
X   if ((mp = mapfile(file)) == NULL) {
X       return -1;
X   }
X   hp = mp->e_hdr;
X   if (hflag || aflag) {
X       prthdr(hp);
X       if (!aflag) return 0;
X   }
X   if (hp->a_syms == 0 && !dflag) {
X       fprintf(stderr, "%s: no name list\n", file);
X       return -1;
X   }
X   if (hp->a_syms != 0 && (aflag || !dflag))
X       prtnlist(mp, aflag);
X
X   if (aflag || xflag) {
X       if (hp->a_trsize + hp->a_drsize != 0) {
X           prtarel(mp);
X       } else if (!aflag) {
X           fprintf(stderr, "%s: no relocation\n", file);
X           return -1;
X       }
X   }
X#ifdef sun
X   if (dflag || aflag)
X       prtdyn(mp);
X#endif
X
X   unmapfile(mp);
X   return 0;
X}
X
Xint
Xprtnlist(mp)
Xstruct execmap *mp;
X{
X   int n;
X   struct nlist *nlp;
X
X   n = mp->e_nnlist;
X   nlp = mp->e_nlist;
X   if (!pflag) {
X       nlp = (struct nlist *)malloc(n * sizeof(struct nlist));
X       bcopy((char *)mp->e_nlist, (char *)nlp, n*sizeof(struct nlist));
X       qsort((char *)nlp, n, sizeof(struct nlist),
X               nflag?
X                   (rflag?rncompare:ncompare)
X               :
X                   (rflag?rscompare:scompare));
X   }
X   for(; n; n--, nlp++) {
X       if (aflag ||
X           ((nlp->n_type & N_STAB) == 0 && (
X               (!gflag && !uflag) ||
X               (uflag && (nlp->n_type & N_TYPE) == N_UNDF) ||
X               (gflag && (nlp->n_type & N_EXT))
X               )
X           )
X       ) {
X           prtsym(nlp);
X           fputc('\n', stdout);
X       }
X   }
X   return 0;
X}
X
Xprtarel(mp)
Xstruct execmap *mp;
X{
X#ifdef sun
X   if (mp->e_hdr->a_trsize) {
X       printf("\nText relocations:\n");
X       prtrel(mp->e_trel, mp->e_ntrel, mp->e_nlist);
X   }
X   if (mp->e_hdr->a_drsize) {
X       printf("\nData relocations:\n");
X       prtrel(mp->e_drel, mp->e_ndrel, mp->e_nlist);
X   }
X#else
X   fprintf(stderr, "\nrelocations not done\n");
X#endif
X}
X
X#ifdef sun
Xint
Xprtdyn(mp)
Xstruct execmap *mp;
X{
X   struct link_dynamic *dynp;
X   struct link_dynamic_2 *ldp;
X   struct ld_debug *lddp;
X   struct link_object *lop = NULL;
X   char *d_rules = NULL;
X   struct nlist *nlp;
X   int i, nnlist, nrel, nplt, ngot;
X   char *strtab;
X   struct reloc_info *rlp;
X   long *got;
X   struct plt {
X       int X1, X2, X3;
X   } *plt;
X
X   if (!mp->e_hdr->a_dynamic) {
X       fprintf(stderr, "Not dynamic\n");
X       return -1;
X   }
X   /* By convention, __DYNAMIC is first data item */
X   dynp = (struct link_dynamic *)mp->e_data;
X   /*
X    * Various tables and structures are imbedded in text and data segment
X    * In text: relocation table, hash table, stab table, string table,
X    *      needed link_objects, library search rules
X    * In data: link_dynamic[-2], debugger iface, global offset table,
X    *      procedure linkage table
X    */
X    /* First, some relocations of our own */
X   ldp = (struct link_dynamic_2 *)
X       ((long)dynp->ld_un.ld_2 - N_DATADDR(*(mp->e_hdr)) + (long)mp->e_data);
X   lddp = (struct ld_debug *)
X       ((long)dynp->ldd - N_DATADDR(*(mp->e_hdr)) + (long)mp->e_data) ;
X   if (ldp->ld_need)
X       lop = (struct link_object *)(ldp->ld_need + mp->e_text);
X   if (ldp->ld_rules)
X       d_rules = (char *)(ldp->ld_rules + (long)mp->e_text);
X   got = (long *)(ldp->ld_got - N_DATADDR(*(mp->e_hdr)) + (long)mp->e_data);
X   ngot = (ldp->ld_plt - ldp->ld_got) / sizeof(long);
X
X   plt = (struct plt *)(ldp->ld_plt - N_DATADDR(*(mp->e_hdr)) + (long)mp->e_data);
X   nplt = ldp->ld_plt_sz / sizeof(struct plt);
X
X   rlp = (struct reloc_info *)(ldp->ld_rel + (long)mp->e_text);
X   nrel = (ldp->ld_hash - ldp->ld_rel) / sizeof(struct reloc_info);
X
X   nlp = (struct nlist *)(ldp->ld_stab + (long)mp->e_text);
X   nnlist = (ldp->ld_symbols - ldp->ld_stab) / sizeof(struct nlist);
X   strtab = (char *)((long)mp->e_text + ldp->ld_symbols);
X   for (i = 0; i < nnlist; i++) {
X       if (nlp[i].n_un.n_strx < ldp->ld_symb_size)
X           nlp[i].n_un.n_name = strtab + nlp[i].n_un.n_strx;
X       else
X           nlp[i].n_un.n_name = "sym_???";
X   }
X
X   printf("Dynamic name list (%u):\n", nnlist);
X   for(i = 0; i < nnlist; i++) {
X       prtsym(&nlp[i]);
X       fputc('\n', stdout);
X   }
X
X   printf("\nProcedure linkage table (%u):\n", nplt);
X   printf(" PLT  GOT  DYN\n");
X   /* first entry in the plt contains entry point for ld.so */
X   for(i = 1; i < nplt; i++) {
X       printf("%4x %4x %4x ", i*sizeof(struct plt),
X           i*sizeof(struct plt) + (char *)plt - (char *)got,
X           i*sizeof(struct plt) + (char *)plt - (char *)dynp);
X       prtrel(&rlp[(plt[i].X3 & 0xffff)], 1, nlp);
X   }
X
X   printf("\nGlobal offset table (%u):\n", ngot);
X   for(i = 0; i < ngot; i++) {
X       printf(" [got entry: %x]\n", got[i]);
X   }
X
X   if (aflag || xflag) {
X       printf("\nDynamic relocations (%u):\n", nrel);
X       prtrel(rlp, nrel, nlp);
X   }
X
X   if (lop) {
X       printf("\nNeeded libs:\n");
X       while (1) {
X           if (lop->lo_library)
X               printf("\tlib%s.so.%u.%u (SEARCHED)\n",
X                   lop->lo_name + mp->e_text,
X                   lop->lo_major, lop->lo_minor);
X           else
X               printf("\t%s\n", lop->lo_name + mp->e_text);
X           if (lop->lo_next == 0)
X               break;
X           lop = (struct link_object *)(lop->lo_next + mp->e_text);
X       }
X   }
X   if (d_rules)
X       printf("Search rules: %s\n", d_rules);
X   return 0;
X}
X#endif
END_OF_FILE
if test 5881 -ne `wc -c <'nm.c'`; then
    echo shar: \"'nm.c'\" unpacked with wrong size!
fi
# end of 'nm.c'
fi
if test -f 'nmd.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'nmd.1'\"
else
echo shar: Extracting \"'nmd.1'\" \(1966 characters\)
sed "s/^X//" >'nmd.1' <<'END_OF_FILE'
X.\" @(#)nmd.1  1.1 92/01/22
X.TH NMD 1 "January 1992"
X.SH NAME
Xnmd \- print symbol name list
X.SH SYNOPSIS
X.B nmd
X[
X.B \-dgnpruxa
X]
X[
X.RI [ " filename " "] .\|.\|."
X.SH DESCRIPTION
X.IX  "nmd command"  ""  "\fLnmd\fP \(em display name list"
X.IX  display "name list of object file or library \(em \fLnmd\fP"
X.IX  "programming tools"  nmd  ""  "\fLnm\fP \(em display name list"
X.B nmd
Xprints the name list (symbol table) of each object
X.I filename
Xin the argument list.
XIf no
X.I filename
Xis given, the symbols in
X.B a.out
Xare listed.
X.SS Output Format
X.LP
XEach symbol name is preceded by its value (blanks if undefined)
Xand one of the letters:
X.TP
X.B A
Xabsolute
X.TP
X.B B
Xbss segment symbol
X.TP
X.B C
Xcommon symbol
X.TP
X.B D
Xdata segment symbol
X.TP
X.B f
Xfilename
X.TP
X.B t
Xa static function symbol
X.TP
X.B  T
Xtext segment symbol
X.TP
X.B U
Xundefined
X.TP
X.B \-
Xdebug, giving symbol table entries (see
X.B \-a
Xbelow)
X.LP
XThe type letter is upper case if the symbol is external, and
Xlower case if it is local.  The output is sorted alphabetically.
X.SH OPTIONS
X.TP
X.B  \-a
XPrint all symbols.
X.TP
X.B  \-d
XPrint dynamic linker information. This includes
Xthe name list and Procedure Linkage Table entries.
X.TP
X.B  \-g
XPrint only global (external) symbols.
X.TP
X.B \-n
XSort numerically rather than alphabetically.
X.TP
X.B  \-o
XPrepend file or archive element name to
Xeach output line rather than only once.
X.TP
X.B  \-p
XDo not sort; print in symbol-table order.
X.TP
X.B  \-r
XSort in reverse order.
X.TP
X.B  \-x
XPrint relocation tables.
X.TP
X.B  \-u
XPrint only undefined symbols.
X.SH EXAMPLE
X.IP
X.B example% nmd
X.LP
Xprints the symbol list of the file named
X.BR  a.out ,
Xthe default output file for the
X.BR C ,
Xcompiler.
X.SH SEE ALSO
X.BR as (1),
X.BR cc (1V),
X.BR ld (1),
X.BR a.out (5),
X.SH BUGS
X.LP
XArchive files (see ar(1)) are not supported.
X.LP
XPrinting of relocation tables is not complete.
X.LP
XThe
X.B \-g,
X.B \-u,
X.B \-n,
Xand
X.B \-r
Xoptions do not apply when the
X.B \-d
Xoption is in effect.
END_OF_FILE
if test 1966 -ne `wc -c <'nmd.1'`; then
    echo shar: \"'nmd.1'\" unpacked with wrong size!
fi
# end of 'nmd.1'
fi
if test -f 'unstrip.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'unstrip.1'\"
else
echo shar: Extracting \"'unstrip.1'\" \(1386 characters\)
sed "s/^X//" >'unstrip.1' <<'END_OF_FILE'
X.\" @(#)unstrip.1  1.2 92/01/22
X.TH UNSTRIP 1 "January 1992"
X.SH NAME
Xunstrip \- restore symbols and relocation bits in a dynamically linked object file
X.SH SYNOPSIS
X.B unstrip [-h] [-n] [-r]
X.IR filename .\|.\|.
X.SH DESCRIPTION
X.IX  "unstrip command"  ""  "\fLunstrip\fP \(em restore symbols and relocation bits"
X.IX  "programming tools"  strip  ""  "\fLstrip\fP \(em restore symbols and relocation bits"
X.IX  "object file"  strip  ""  "\fLstrip\fP \(em restore symbols and relocation bits"
X.LP
X.B unstrip
Xexternalizes the symbol
Xtable and relocation bits ordinarily provided by the linker
Xin the data segment of a dynamically linked executable.
XThis is useful to debug a program after a it has been stripped to save space.
X.LP
X.B unstrip
Xfixes up the symbol table for the benefit of
X.B adb
Xin such a way as to show calls to functions in shared
Xobjects symbolically (eg. ``call printf'') as opposed to
Xindirections through the Procedure Linkage Table
X(such as ``call __DYNAMIC + 0x1234'').
X.SH OPTIONS
X.TP
X.BI \-h
XShow header information of newly created object file.
X.TP
X.BI \-n
XDo not fix up symbol table (leaves functions in shared objects show
Xas ``undefined'' when viewed with
X.BR nm (1)).
X.TP
X.BI \-r
XDo not pull up relocation information.
X.SH SEE ALSO
X.BR ld (1),
X.BR adb (1),
X.BR strip (1),
X.BR nm (1),
X.BR a.out (5),
X.BR link (5)
X.SH BUGS
X.LP
XOverwrites existing symbol table.
END_OF_FILE
if test 1386 -ne `wc -c <'unstrip.1'`; then
    echo shar: \"'unstrip.1'\" unpacked with wrong size!
fi
# end of 'unstrip.1'
fi
if test -f 'unstrip.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'unstrip.c'\"
else
echo shar: Extracting \"'unstrip.c'\" \(6328 characters\)
sed "s/^X//" >'unstrip.c' <<'END_OF_FILE'
X/*
X * @(#)unstrip.c   1.2 92/01/22    - put symbol table of stripped dynamically linked a.out back into place
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <string.h>
X#include <fcntl.h>
X#include <a.out.h>
X#include <sys/stat.h>
X#include <link.h>
X#include "defs.h"
X
Xextern char *malloc();
X
Xstatic hflag, nflag, rflag;
Xstruct execmap *makesymbols();
X
Xmain(argc, argv)
Xchar *argv[];
X{
X   int c, status = 0;
X   extern int optind;
X   extern char *optarg;
X
X   while ((c = getopt(argc, argv, "hnr")) != EOF) {
X       switch (c) {
X       case 'n':
X           nflag++;
X           break;
X       case 'h':
X           hflag++;
X           break;
X       case 'r':
X           rflag++;
X           break;
X       default:
X           fprintf(stderr, "Usage: %s [file ...]\n", argv[0]);
X           return -1;
X           break;
X       }
X   }
X   if (argc <= optind) {
X       status = unstrip("a.out") == -1;
X   } else if (argc == optind + 1) {
X       status = unstrip(argv[optind]) == -1;
X   } else {
X       for (; optind < argc; optind++) {
X           printf("\n%s:\n", argv[optind]);
X           status |= unstrip(argv[optind]) == -1;
X       }
X   }
X   return status;
X}
X
Xint
Xunstrip(file)
Xchar *file;
X{
X   int fd, res;
X   struct exec *hp;
X   struct execmap *mp, *nmp;
X
X   if ((mp = mapfile(file)) == NULL) {
X       return -1;
X   }
X   hp = mp->e_hdr;
X   if (!hp->a_dynamic) {
X       fprintf(stderr, "%s: Not dynamic\n", file);
X       (void)unmapfile(mp);
X       return -1;
X   }
X   if (hp->a_trsize != 0 || hp->a_drsize != 0) {
X       fprintf(stderr, "Warning: %s contains relocation table\n", file);
X       (void)unmapfile(mp);
X       return -1;
X   }
X   if ((nmp = makesymbols(mp)) == NULL) {
X       fprintf(stderr, "Couldn't get symbols from %s\n", file);
X       (void)unmapfile(mp);
X       return -1;
X   }
X   if ((fd = open(file, O_WRONLY|O_CREAT, 0666)) == -1) {
X       perror("open");
X       res = -1;
X       goto quit;
X   }
X
X   if (writemap(fd, nmp) < 0 || close(fd) < 0) {
X       perror("writemap");
X       res = -1;
X       goto quit;
X   }
X   res = 0;
X
X   if (hflag)
X       prthdr(nmp->e_hdr);
X
Xquit:
X   if (nmp->e_nlist) (void)free((char *)nmp->e_nlist);
X   if (nmp->e_sym) (void)free((char *)nmp->e_sym);
X   if (nmp->e_trel) (void)free((char *)nmp->e_trel);
X#if 0
X   (void)free((char *)nmp->e_hdr);
X#endif
X   (void)free((char *)nmp);
X   if (unmapfile(mp) < 0)
X       return -1;;
X   return res;
X}
X
Xint
Xwritemap(fd, mp)
Xstruct execmap *mp;
X{
X   struct exec *hp = mp->e_hdr;
X   unsigned long n;
X
X   if (write(fd, (char *)hp, sizeof(*hp)) < sizeof(*hp))
X       return -1;
X
X   if (lseek(fd, hp->a_text + hp->a_data + N_TXTOFF(*hp), 0) < 0)
X       return -1;
X
X   if (hp->a_trsize &&
X       write(fd, (char *)mp->e_trel, hp->a_trsize) < hp->a_trsize)
X       return -1;
X
X   if (hp->a_drsize &&
X       write(fd, (char *)mp->e_drel, hp->a_drsize) < hp->a_drsize)
X       return -1;
X
X   if (write(fd, (char *)mp->e_nlist, hp->a_syms) < hp->a_syms)
X       return -1;
X
X   bcopy(mp->e_sym, (char *)&n, sizeof n);
X   if (write(fd, mp->e_sym, n) < n)
X       return -1;
X
X   return 0;
X}
X
Xstruct execmap *
Xmakesymbols(mp)
Xstruct execmap *mp;
X{
X   struct execmap *nmp;
X   struct link_dynamic *dynp;
X   struct link_dynamic_2 *ldp;
X   struct ld_debug *lddp;
X   struct link_object *lop;
X   char *d_rules;
X   struct nlist *nlp;
X   char *strtab;
X   int i, nnlist, nrel, nplt, ngot;
X   struct reloc_info *rlp;
X   long *got;
X   struct plt {
X       int X1, X2, X3;
X   } *plt;
X
X   /* By convention, __DYNAMIC is first data item */
X   dynp = (struct link_dynamic *)mp->e_data;
X   /*
X    * Various tables and structures are imbedded in text and data segment
X    * In text: relocation table, hash table, stab table, string table
X    * In data: link_dynamic[_2], debugger iface, global offset table,
X    *      procedure linkage table
X    */
X    /* First, some relocations of our own */
X   ldp = (struct link_dynamic_2 *)
X       ((long)dynp->ld_un.ld_2 - N_DATADDR(*(mp->e_hdr)) + (long)mp->e_data);
X   lddp = (struct ld_debug *)
X       ((long)dynp->ldd - N_DATADDR(*(mp->e_hdr)) + (long)mp->e_data) ;
X   lop = (struct link_object *)(ldp->ld_need + mp->e_text);
X   d_rules = (char *)(ldp->ld_rules + (long)mp->e_text);
X   got = (long *)(ldp->ld_got - N_DATADDR(*(mp->e_hdr)) + (long)mp->e_data);
X   ngot = (ldp->ld_plt - ldp->ld_got) / sizeof(long);
X
X   plt = (struct plt *)(ldp->ld_plt - N_DATADDR(*(mp->e_hdr)) + (long)mp->e_data);
X   nplt = ldp->ld_plt_sz / sizeof(struct plt);
X
X   rlp = (struct reloc_info *)(ldp->ld_rel + (long)mp->e_text);
X   nrel = (ldp->ld_hash - ldp->ld_rel) / sizeof(struct reloc_info);
X
X   nlp = (struct nlist *)(ldp->ld_stab + (long)mp->e_text);
X   nnlist = (ldp->ld_symbols - ldp->ld_stab) / sizeof(struct nlist);
X   strtab = (char *)((long)mp->e_text + ldp->ld_symbols);
X
X   /* allocate new map */
X   if ((nmp = (struct execmap *)calloc(1, sizeof(*nmp))) == NULL) {
X       fprintf(stderr, "No memory\n");
X       return NULL;
X   }
X   /* allocate new stab, symbol and reloc tables */
X   nmp->e_nlist = (struct nlist *)malloc(ldp->ld_symbols - ldp->ld_stab);
X
X   if (nmp->e_nlist == NULL) {
X       fprintf(stderr, "Out of memory\n");
X       return NULL;
X   }
X   bcopy((char *)nlp, (char *)nmp->e_nlist, ldp->ld_symbols - ldp->ld_stab);
X
X   for(i = 0; i < nnlist; i++) {
X       /* Fix up sym index */
X       nmp->e_nlist[i].n_un.n_strx += 4;
X   }
X   nmp->e_sym = malloc(ldp->ld_symb_size + 4);
X   if (nmp->e_sym == NULL) {
X       fprintf(stderr, "Out of memory\n");
X       return NULL;
X   }
X   *(long *)nmp->e_sym = ldp->ld_symb_size + 4;
X   bcopy(strtab, nmp->e_sym + 4, ldp->ld_symb_size);
X
X   nmp->e_trel = (struct reloc_info *)malloc((nplt-1)*sizeof(struct reloc_info));
X   if (nmp->e_trel == NULL) {
X       fprintf(stderr, "Out of memory\n");
X       return NULL;
X   }
X   /* first entry in the plt contains entry point for ld.so */
X   for(i = 1; i < nplt; i++) {
X       bcopy((char *)&rlp[(plt[i].X3 & 0xffff)],
X           (char *)&nmp->e_trel[i-1], sizeof(struct reloc_info));
X   }
X
X   /* Fix up nlist for adb */
X   if (!nflag)
X       for(i = 1; i < nplt; i++) {
X           struct reloc_info *rp = &rlp[(plt[i].X3 & 0xffff)];
X           struct nlist *np;
X
X           if (rp->r_extern == 0) {
X               fprintf(stderr, "Weird reloc\n");
X               continue;
X           }
X           np = &nmp->e_nlist[rp->r_index];
X           np->n_value = ldp->ld_plt + i*sizeof(*plt);
X           np->n_type = N_TEXT | (np->n_type & N_EXT);
X       }
X
X#if 0
X   if ((nmp->e_hdr = (struct exec *)calloc(1, sizeof *nmp->e_hdr)) == NULL) {
X       fprintf(stderr, "Out of memory\n");
X       return NULL;
X   }
X#endif
X   nmp->e_hdr = mp->e_hdr;
X
X   /* copy header, text, data and bss from old map */
X   nmp->e_text = mp->e_text;
X   nmp->e_data = mp->e_data;
X
X   /* Add symbols to a.out header */
X   nmp->e_hdr->a_syms = nnlist*sizeof(struct nlist);
X   if (rflag)
X       nmp->e_hdr->a_trsize = (nplt-1)*sizeof(struct reloc_info);
X   else
X       nmp->e_hdr->a_trsize = 0;
X
X   /* No data relocations */
X   nmp->e_hdr->a_drsize = 0;
X   return nmp;
X}
END_OF_FILE
if test 6328 -ne `wc -c <'unstrip.c'`; then
    echo shar: \"'unstrip.c'\" unpacked with wrong size!
fi
# end of 'unstrip.c'
fi
if test -f 'util.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'util.c'\"
else
echo shar: Extracting \"'util.c'\" \(7655 characters\)
sed "s/^X//" >'util.c' <<'END_OF_FILE'
X/*
X * @(#)util.c  1.2 92/01/22    - a.out & nlist related routines
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <string.h>
X#include <malloc.h>
X#include <fcntl.h>
X#include <a.out.h>
X#ifndef sun
X#include <nlist.h>
X#endif
X#include <sys/stat.h>
X#ifdef sun
X#include <sys/mman.h>
X#include <link.h>
X#endif
X#include "defs.h"
X
Xvoid
Xprthdr(hp)
Xstruct exec *hp;
X{
X#ifdef sun
X   (void)printf(hp->a_dynamic?"DYNAMIC":"STATIC");
X   (void)printf(" Magic %o Version %u Mach %o\n",
X           hp->a_magic, hp->a_toolversion,
X           hp->a_machtype);
X#else
X   (void)printf(" Magic %o\n", hp->a_magic);
X#endif
X   (void)printf("Text %u Data %u Bss %u Syms %u Entry %#x\n",
X       hp->a_text, hp->a_data, hp->a_bss, hp->a_syms, hp->a_entry);
X   (void)printf("Trsize %u Drsize %u\n", hp->a_trsize, hp->a_drsize);
X   return;
X}
X
Xint
Xncompare(nlp1, nlp2)
Xstruct nlist *nlp1, *nlp2;
X{
X   return nlp1->n_value - nlp2->n_value;
X}
X
Xint
Xscompare(nlp1, nlp2)
Xstruct nlist *nlp1, *nlp2;
X{
X   return strcmp(nlp1->n_un.n_name, nlp2->n_un.n_name);
X}
X
Xint
Xrncompare(nlp1, nlp2)
Xstruct nlist *nlp1, *nlp2;
X{
X   return nlp2->n_value - nlp1->n_value;
X}
X
Xint
Xrscompare(nlp1, nlp2)
Xstruct nlist *nlp1, *nlp2;
X{
X   return strcmp(nlp2->n_un.n_name, nlp1->n_un.n_name);
X}
X
Xvoid
Xprtsym(nlp)
Xstruct nlist *nlp;
X{
X   char c;
X
X   switch (nlp->n_type & N_TYPE) {
X   case N_UNDF:
X       if (nlp->n_value && (nlp->n_type & N_EXT))
X           c = 'C';
X       else
X           c = 'U';
X       break;
X   case N_ABS: c = 'A'; break;
X   case N_TEXT: c = 'T'; break;
X   case N_DATA: c = 'D'; break;
X   case N_BSS: c = 'B'; break;
X   case N_COMM: c = 'C'; break;
X   case N_FN: c = 'F'; break;
X   default: c = '?'; break;
X   }
X   if (!(nlp->n_type & N_EXT))
X       c = tolower(c);
X
X#ifdef DEBUG
X   (void)printf("type:%#x other:%#x desc:%#x",
X       nlp->n_type, nlp->n_other, nlp->n_desc);
X#endif
X   (void)printf(tolower(c)=='u'?"\t":N_FORMAT, nlp->n_value);
X   (void)printf(" %c", c);
X   (void)printf(" %s", nlp->n_un.n_name);
X
X   return;
X}
X
Xstatic char *
Xreltyp(v)
Xenum reloc_type v;
X{
X#ifdef sun
X#ifdef sparc
X   static struct {
X       enum reloc_type v;
X       char    *s;
X   } *x, tab[] = {
X       RELOC_8,    "R_8",
X       RELOC_16,   "R_16",
X       RELOC_32,   "R_32",
X       RELOC_DISP8,    "DISP8",
X       RELOC_DISP16,   "DISP16",
X       RELOC_DISP32,   "DISP32",
X       RELOC_WDISP30,  "WDISP30",
X       RELOC_WDISP22,  "WDISP22",
X       RELOC_HI22, "HI22",
X       RELOC_22,   "R_22",
X       RELOC_13,   "R_13",
X       RELOC_LO10, "LO10",
X       RELOC_SFA_BASE, "SFA_BASE",
X       RELOC_SFA_OFF13,    "SFA_OFF13",
X       RELOC_BASE10,   "BASE10",
X       RELOC_BASE13,   "BASE13",
X       RELOC_BASE22,   "BASE22",
X       RELOC_PC10, "PC10",
X       RELOC_PC22, "PC22",
X       RELOC_JMP_TBL,  "JMP_TBL",
X       RELOC_SEGOFF16, "SEGOFF16",
X       RELOC_GLOB_DAT, "GLOB_DAT",
X       RELOC_JMP_SLOT, "JMP_SLOT",
X       RELOC_RELATIVE, "RELATIVE",
X       (enum reloc_type)0, NULL,
X   };
X
X   for (x=tab; x->s; x++) {
X       if (x->v == v)
X           return x->s;
X   }
X#endif /* sparc */
X#endif /* sun */
X   return "RELOC_???";
X}
X
X#ifdef sun
Xvoid
Xprtrel(rp, nrp, nlp)
Xstruct reloc_info *rp;
Xint nrp;
Xstruct nlist *nlp;
X{
X   for (; nrp; nrp--, rp++) {
X       (void)printf("%08x %s", rp->r_address, reltyp(rp->r_type));
X       if (rp->r_extern) {
X#ifdef DEBUG
X           (void)printf(" -- index %04x --", rp->r_index);
X#endif
X           (void)printf(" [");
X           (void)prtsym(nlp + rp->r_index, 0);
X           (void)printf("]", rp->r_index);
X       } else {
X           (void)printf(" ntype %x", rp->r_index);
X       }
X       (void)printf(" + %d (%1$x)\n", rp->r_addend);
X   }
X
X   return;
X}
X#endif
X
Xstruct execmap *
Xmapfile(file)
Xchar *file;
X{
X   struct execmap *mp;
X   struct exec *hp;
X   int fd;
X   unsigned long strsize;
X   struct nlist *nlp;
X   struct stat stb;
X   caddr_t addr;
X
X   if ((fd = open(file, O_RDONLY)) == -1) {
X       fprintf(stderr, "%s: ", file); perror("open");
X       return NULL;
X   }
X   if ((mp = (struct execmap *)calloc(1, sizeof(*mp))) == NULL) {
X       fprintf(stderr, "mapfile: no memory\n");
X       goto errx;
X   }
X#ifdef sun
X   if (fstat(fd, &stb) < 0) {
X       perror("stat");
X       goto errx;
X   }
X   if ((addr = mmap((caddr_t)0, stb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0)) == (caddr_t)-1) {
X       perror("mmap");
X       goto errx;
X   }
X   mp->e_mlen = stb.st_size;
X   hp = mp->e_hdr = (struct exec *)addr;
X   if (N_BADMAG(*hp)) {
X       fprintf(stderr, "%s: bad format\n", file);
X       goto errx;
X   }
X   mp->e_text = addr + N_TXTOFF(*hp);
X   mp->e_data = addr + N_DATOFF(*hp);
X   if (hp->a_syms == 0) {
X       (void)close(fd);
X       return mp;
X   }
X
X   /* Symbol table */
X   mp->e_nnlist = hp->a_syms / sizeof(struct nlist);
X   mp->e_nlist = (struct nlist *)(addr + N_SYMOFF(*hp));
X   mp->e_sym = (char *)(addr + N_STROFF(*hp));
X   strsize = *((unsigned long *)mp->e_sym);
X   if (strsize < 4) {
X       fprintf(stderr, "Incorrect string table size.\n");
X       goto errx;
X   }
X   if (hp->a_text+hp->a_data+hp->a_syms+
X       hp->a_trsize+hp->a_drsize + strsize > mp->e_mlen) {
X       fprintf(stderr, "Giant string table.\n");
X       goto errx;
X   }
X
X   /* Relocation tables */
X   if (hp->a_trsize != 0) {
X       mp->e_ntrel = (hp->a_trsize) / sizeof(struct reloc_info);
X       mp->e_trel = (struct reloc_info *)(addr + N_TRELOFF(*hp));
X   }
X   if (hp->a_drsize != 0) {
X       mp->e_ndrel = (hp->a_drsize) / sizeof(struct reloc_info);
X       mp->e_drel = (struct reloc_info *)(addr + N_DRELOFF(*hp));
X   }
X
X#else sun
X
X   if ((hp = mp->e_hdr = (struct exec *)malloc(sizeof(struct exec))) == NULL) {
X       fprintf(stderr, "No memory for header.\n");
X       goto errx;
X   }
X   if (read(fd, (char *)hp, sizeof(struct exec)) < sizeof(struct exec)) {
X       perror("read");
X       goto errx;
X   }
X   if (N_BADMAG(*hp)) {
X       fprintf(stderr, "%s: bad format\n", file);
X       goto errx;
X   }
X   if (hp->a_syms == 0) {
X       (void)close(fd);
X       return mp;
X   }
X   mp->e_nnlist = hp->a_syms / sizeof(struct nlist);
X   if ((mp->e_nlist = (struct nlist *)malloc(hp->a_syms)) == NULL) {
X       fprintf(stderr, "No memory for nlist.\n");
X       goto errx;
X   }
X
X   /* read symbol table */
X   if (lseek(fd, N_SYMOFF(*hp), 0) < 0) {
X       perror("seek");
X       goto errx;
X   }
X   if (read(fd, (char *)mp->e_nlist, hp->a_syms) < hp->a_syms) {
X       perror("read nlist");
X       goto errx;
X   }
X   if (lseek(fd, N_STROFF(*hp), 0) < 0) {
X       perror("seek");
X       goto errx;
X   }
X   if (read(fd, (char *)&strsize, 4) < 4) {
X       perror("read strsize");
X       goto errx;
X   }
X   if (strsize < 4) {
X       fprintf(stderr, "Incorrect string table size.\n");
X       goto errx;
X   }
X   if (lseek(fd, -4, 1) < 0) {
X       perror("seek");
X       goto errx;
X   }
X   if ((mp->e_sym = malloc(strsize)) == NULL) {
X       fprintf(stderr, "No memory for strings.\n");
X       goto errx;
X   }
X   if (read(fd, mp->e_sym, strsize) < strsize) {
X       perror("read strings");
X       goto errx;
X   }
X
X   /* read relocation tables */
X   if (hp->a_trsize != 0) {
X       mp->e_ntrel = (hp->a_trsize) / sizeof(struct reloc_info);
X       if ((mp->e_trel = (struct reloc_info *)malloc(hp->a_trsize)) == 0) {
X           fprintf(stderr, "No memory for reloc_info.\n");
X           goto errx;
X       }
X       if (lseek(fd, N_TRELOFF(*hp), 0) < 0) {
X           perror("seek");
X           goto errx;
X       }
X       if (read(fd, (char *)mp->e_trel, hp->a_trsize) < hp->a_trsize) {
X           perror("read reloc_info");
X           goto errx;
X       }
X   }
X   if (hp->a_drsize != 0) {
X       mp->e_ndrel = (hp->a_drsize) / sizeof(struct reloc_info);
X       if ((mp->e_drel = (struct reloc_info *)malloc(hp->a_drsize)) == 0) {
X           fprintf(stderr, "No memory for reloc_info.\n");
X           goto errx;
X       }
X       if (lseek(fd, N_DRELOFF(*hp), 0) < 0) {
X           perror("seek");
X           goto errx;
X       }
X       if (read(fd, (char *)mp->e_drel, hp->a_drsize) < hp->a_drsize) {
X           perror("read reloc_info");
X           goto errx;
X       }
X   }
X#endif sun
X
X   (void)close(fd);
X   /* convert string table offset to char pointer, in situ */
X   for (nlp = mp->e_nlist; nlp < mp->e_nlist + mp->e_nnlist; nlp++) {
X       if (nlp->n_un.n_strx > 0 && nlp->n_un.n_strx < strsize)
X           nlp->n_un.n_name = mp->e_sym + nlp->n_un.n_strx;
X       else
X           nlp->n_un.n_name = "sym_???";
X   }
X   return mp;
X
Xerrx:
X   (void)close(fd);
X   return NULL;
X}
X
Xint
Xunmapfile(mp)
Xstruct execmap *mp;
X{
X#ifdef sun
X   if (munmap(mp->e_hdr, mp->e_mlen) < 0) {
X       perror("munmap");
X       return -1;
X   }
X#endif
X   (void)free((char *)mp);
X   return 0;
X}
X
END_OF_FILE
if test 7655 -ne `wc -c <'util.c'`; then
    echo shar: \"'util.c'\" unpacked with wrong size!
fi
# end of 'util.c'
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
    MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have the archive.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0


: To unbundle, sh this file
echo x - mksym.c 1>&2
sed 's/^X//' >mksym.c <<'@@@End of mksym.c'
X/* 
X * Copy just the symbol tables from an application for debugging.
X * (BSD format: must also copy "string table", the actual name entries).
X *
X * $Id: mksym.c,v 1.4 92/02/03 17:28:55 ian Exp $
X *
X * Run in makefile just BEFORE stripping.
X * usage: mksyms a.out # writes on a.out.sym
X * e.g.,
X * $(CC) $(AEOBJS) $(AELIBS) $(SYSLIBS) -o ae_2.24
X * mksyms ae_2.24 # creates ae_2.24.sym
X * strip ae_2.24
X *
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <a.out.h>
X
X#define    MAXSTR  1000
X
Xint    debug;
Xstatic struct stat statbuf;
Xchar   *progname;
Xvoid   error(), exit();
Xvoid   closein(), closeout();
X
Xstatic char *txbuf, *dtbuf,    /* used to read text and data segments */
X   *symbuf, *strbuf,   /* symbols */
X   *txrldbuf,      /* text rld */
X   *dtrldbuf;      /* data rld */
Xlong symtabsize, strtabsize;   /* the only ones we need to compute */
Xlong pigsize;          /* pessimal assumption here... */
Xchar *malloc();
X
Xstatic int ifd, ofd;
Xstatic struct exec filhdr;
Xextern int errno;
X
X#define max(x,y) (x>y?x:y)
X
X/*
X * main - parse arguments and handle options
X */
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X   int c, errflg = 0, keepem = 0;
X   extern int optind;
X   extern char *optarg;
X
X   progname = argv[0];
X
X   pigsize = getpagesize();
X
X   while ((c = getopt(argc, argv, "d")) != EOF)
X       switch (c) {
X       case 'd':
X           ++debug;
X           break;
X       default:
X           errflg++;
X           break;
X       }
X   if (errflg) {
X       (void) fprintf(stderr, "usage: %s xxx [file] ...\n", progname);
X       exit(2);
X   }
X
X   if (argc <= optind) {
X       fprintf(stderr, "usage: %s a.out > a.syms\n", progname);
X       exit(3);
X   }
X
X   for (; optind < argc; optind++) {
X           if (stat(argv[optind], &statbuf) != 0) {
X               fprintf(stderr,
X                   "can't fstat %s", argv[optind]);
X               continue;
X           }
X           if ((statbuf.st_mode & S_IFMT)==S_IFDIR) {
X               fprintf(stderr,
X                   "%s is a directory!", argv[optind]);
X               continue;
X           }
X           process(argv[optind], debug, statbuf.st_size);
X       }
X
X   exit(0);
X}
X
Xprocess(inname, debug, fsize)
Xchar *inname;
Xint debug;
Xlong fsize;
X{
X   char outname[MAXSTR+1];
X   char *AllTheRest;
X   long LengthOfTheRest;
X
X   strncpy(outname, inname, MAXSTR);
X   strncat(outname, ".sym", MAXSTR);
X
X   /* PART ONE -- read the old format in, and convert */
X   openin(inname, fsize);  /* sets globals s* based on exec hdr */
X
X#define bufalloc(buf,bufsize) if ((buf = malloc((unsigned)bufsize)) == NULL) { \
X       fprintf(stderr, "%s: can't malloc memory for text/data\n", \
X           progname); \
X       exit(4); \
X       }
X
X   LengthOfTheRest = fsize - N_SYMOFF(filhdr);
X   bufalloc(AllTheRest, LengthOfTheRest);
X   if (lseek(ifd, N_SYMOFF(filhdr), 0) < 0) {
X       perror("lseek to start of symbol table on input");
X       exit(1);
X   }
X
X   {
X       long i = read(ifd, AllTheRest, LengthOfTheRest);
X
X       if (i != LengthOfTheRest) {
X           extern int errno;
X           int e = errno;
X
X           fprintf(stderr,
X               "%s: %s: %ld bytes instead of %ld returned by ",
X               progname, inname, i, LengthOfTheRest);
X           errno =e;
X           perror("read()");
X           exit(1);
X       }
X
X       closein();
X       openout(outname);
X
X       i = write(ofd, AllTheRest, LengthOfTheRest);
X
X       if (i != LengthOfTheRest) {
X           extern int errno;
X           int e = errno;
X
X           fprintf(stderr,
X               "%s: %s: %ld bytes instead of %ld returned by ",
X               progname, inname, i, LengthOfTheRest);
X           errno =e;
X           perror("write()");
X           exit(1);
X       }
X   }
X
X
X#if 0
X   bufalloc(symbuf,   symtabsize);
X   getsyms(symbuf);
X
X   bufalloc(strbuf,   strtabsize);
X   getstrs(strbuf);
X
X   closein();
X
X   /* PART TWO - write the output file */
X   openout();
X   putsyms(symbuf);
X   putstrs(strbuf);
X#endif
X   closeout();
X
X   /* CLEAN UP */
X   if (txbuf) free(txbuf);
X   if (dtbuf) free(dtbuf);
X   if (symbuf) free(symbuf);
X   if (strbuf) free(strbuf);
X   if (AllTheRest) free(AllTheRest);
X}
X
X
X/*
X * Open a a.out for input, read header, return sizes of the
X * (text, data, sym, trld, drld) segments so a buffer can be allocated.
X */
Xopenin(fn, filesize)
Xchar *fn;
Xlong filesize;
X{
X   int i;
X
X   if ((ifd = open(fn, 0)) <0) {
X       fprintf(stderr, "%s: error opening %s\n", progname, fn);
X       exit(101);
X   }
X
X   if ((i=read(ifd, (char *)&filhdr, sizeof filhdr)) != sizeof filhdr) {
X       int realerrno = errno;
X       fprintf(stderr,
X           "%s: read exec: errno %d, got %d bytes, wanted %d\n",
X           progname, realerrno, i, sizeof filhdr);
X       exit(102);
X   }
X
X   if (N_BADMAG(filhdr)) {
X       fprintf(stderr,
X           "%s: bad magic: magic number %ld\n",
X           progname, filhdr.a_magic);
X       exit(103);
X   }
X
X   /* Get symtabsize and strtabsize from the input file */
X
X   symtabsize = filhdr.a_syms;
X
X   lseek(ifd, N_STROFF(filhdr), 0);
X   if (sizeof strtabsize != read (ifd, &strtabsize, sizeof strtabsize)) {
X       fprintf(stderr, "can't get size of string table\n");
X       return;
X   }
X
X   if (debug) {
X       printf("Name:\t%s\n", fn);
X       printf("Fsize:\t%d\n", filesize);
X       printf("Txtsiz:\t%d\n", filhdr.a_magic);
X       printf("Symsiz:\t%d (from a_syms)\n", symtabsize);
X       printf("strsiz:\t%d (read from file)\n", strtabsize);
X       printf("symstart:\t%ld\n", N_SYMOFF(filhdr));
X   }
X
X   return;
X}
X
Xvoid
Xclosein()
X{
X   (void) close(ifd);
X}
X
Xopenout(outn)
Xchar *outn;
X{
X
X   ofd = creat(outn, 0644);
X   if (ofd<0) {
X       perror(outn);
X       exit(1);
X   }
X
X   if (write(ofd, &filhdr, sizeof filhdr) != sizeof(filhdr)) {
X       perror("write first header");
X       exit(1);
X   }
X
X   if (lseek(ofd, (long)N_SYMOFF(filhdr), 0) < 0) {
X       perror("lseek to start of symbol table on output");
X       exit(1);
X   }
X}
X
Xvoid
Xcloseout()
X{
X   (void) close(ofd);
X}
X
Xgettext(buf)
Xchar *buf;
X{
X   if (lseek(ifd, N_TXTOFF(filhdr), 0) <0) {
X       perror("gettext: lseek");
X       exit(1);
X   }
X   if (read(ifd, (char *)buf, filhdr.a_text) != filhdr.a_text) {
X       perror("gettext: read");
X       exit(1);
X   }
X   return;
X}
X
Xgetdata(buf)
Xchar *buf;
X{
X   if (lseek(ifd, N_DATOFF(filhdr), 0) <0) {
X       perror("getdata: lseek");
X       exit(1);
X   }
X   if (read(ifd, (char *)buf, filhdr.a_data) != filhdr.a_data) {
X       perror("getdata: read");
X       exit(1);
X   }
X   return;
X}
X
Xgetsyms(buf)
Xchar *buf;
X{
X   if (lseek(ifd, N_SYMOFF(filhdr)+sizeof strtabsize, 0) <0) {
X       perror("getsyms: lseek");
X       exit(1);
X   }
X   if (read(ifd, (char *)buf, symtabsize) != symtabsize) {
X       perror("getsyms: read");
X       exit(1);
X   }
X}
X
Xgetstrs(buf)
Xchar *buf;
X{
X   if (lseek(ifd, N_STROFF(filhdr), 0) <0) {
X       perror("getstrs: lseek");
X       exit(1);
X   }
X   if (read(ifd, (char *)buf, strtabsize) != strtabsize) {
X       perror("getstrs: read");
X       exit(1);
X   }
X}
X
Xputsyms(buf)
Xchar *buf;
X{
X   if (lseek(ofd, filhdr.a_text+filhdr.a_data, 0) < 0) {
X       perror("putsyms: seek");
X       exit(1);
X   }
X   if (write(ofd, (char *)buf, symtabsize) != symtabsize) {
X       perror("putsyms: write");
X       exit(1);
X   }
X}
X
Xputstrs(buf)
Xchar *buf;
X{
X   if (write(ofd, (char *)buf, strtabsize) != strtabsize) {
X       perror("putstrs: write");
X       exit(1);
X   }
X}
X
@@@End of mksym.c


-----BEGIN PGP SIGNATURE-----
Version: 2.2

iQCVAgUBLBZikYXSIZRfVL2JAQGgYgP+IL9OhAbjU0ZrsInVimnfOvNrlPArAUyA
27HH8YB1jubAhZk8gWASDQubIBSvxHlvgooGvgO2TNQckD5jWOe6a5LZVpfCDgMH
63E6/HLzL/uRsdhcLUuNM8GnKNhXeYkaZsfTkzYvnl2So5GwHpxu+BBYf+25xrVs
KK18HKxKfcE=
=Z8Kk
-----END PGP SIGNATURE-----