-----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 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 '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 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 X#include X#include X#ifndef sun X#include X#endif X#ifdef sun X#include 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 X#include X#include X#include X#include X#include X#include 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 X#include X#include X#include X#include X#include X#ifndef sun X#include X#endif X#include X#ifdef sun X#include X#include 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 X#include X#include X#include X#include 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-----