2057 lines
57 KiB
Plaintext
2057 lines
57 KiB
Plaintext
-----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-----
|