by Michael Sinz (MKSoft Development) Copyright 1992-2001 - All Rights Reserved FindHit.c |
by Michael Sinz MKSoft Development Copyright 1992-2001 All Rights Reserved Documentation Enforcer Archive Source code for the main Enforcer tool: Source code for the MMU tool: Source code for SegTracker: Source code for FindHit: Source code for RebootOff: Source code for Move4K: Source code for LawBreaker: | /* * Enforcer by * Michael Sinz * * Copyright 1992-2001 * All Rights Reserved * ***************************************************************************** * * * Permission is hereby granted to distribute the Enforcer archive * * containing the executables and documentation for non-commercial purposes * * so long as the archive and its contents are not modified in any way. * * * * Enforcer and related tools are not in the public domain. * * * * Enforcer and related tools may not be distributed for a profit. * * * ***************************************************************************** * */ /* ******* FindHit *************************************************************** * * NAME * FindHit - A tool that can locate the source file and line number * that a SegTracker report happened at. * * SYNOPSIS * FindHit will read the executable file and if there is debugging * information in it, will try to locate the source file and line * number that correspond to the Enforcer hit HUNK/OFFSET. * * FUNCTION * FindHit uses the Lattice/SAS/MetaScope standard 'LINE' * debug hunk to locate the closest line to the hunk/offset given. * Note that this can only happen if the executable has the * LINE debugging turned on. (The LawBreaker program has this * such that you can test this yourself.) * * In SAS/C 6.x, you need to compile with DEBUG=LINE or better * and do not use the link option of NODEBUG. * * In SAS/C 5.x, you need to compile with -d1 or better. * Note that FindHit works with the old SAS/C 5.x 'SRC ' * debugging information too. This is required for -d2 or * higher debugging support. However, I do not have 'SRC ' hunk * documentation and thus FindHit may be very specific to the * SAS/C 5.x version of this hunk. * * In DICE (2.07 registered being the one I tried) the -d1 * debug switch also supports the 'LINE' debug hunk and * works with FindHit. * * In HX68 and CAPE, you need to add the DEBUG directive to * the assembly code program. (See LawBreaker source) * * For other languages, or other versions of the above, please * see the documentation that comes with the language. * * INPUTS * FILE/A - The executable file, with debugging information. * * OFFSETS/A/M - The HEX offset (with or without leading $) * If a hunk number other than the default * is needed, it is expressed as hunk:offset. * The default hunk is that of the last argument * or hunk 0 if no hunk number has been given. * For example: 12 $22 $3:12 22 4:$12 32 $0:$32 * will find information for: * hunk $0, offset $12 * hunk $0, offset $22 * hunk $3, offset $12 * hunk $3, offset $22 * hunk $4, offset $12 * hunk $4, offset $32 * hunk $0, offset $32 * * EXAMPLE * FindHit FooBar $0342 $1:4F2 3:$1A 2C * badcode.c : Line 184 * No line number information for Hunk $1, Offset $4F2 * badcode2.c : Line 12 * badcode2.c : Line 14 * * See the Enforcer documentation about issues dealing with the * exact location of the Enforcer hit. The line given may * not be exactly where the hit happened. * * The way I use this is to always have line debugging turned on * when I compile. This does not change the quality of the code * and takes only a small amount of extra disk space. However, * what I do is to link the program twice: Once to a file called * program.ld which contains all of the debugging information. * Then, I link program.ld to program, stripping debug information. * The command line for SLINK or BLINK is as follows: * * BLINK program.ld TO program NODEBUG * * I keep both of these on hand; with program being the one I * distribute and use. When a hit happens, I can just use program.ld * with FindHit to get the line number and source file that it happened * in. This way you can distribute your software without the debugging * information and still be able to use FindHit on the actual code. * (After all, that link command does nothing but strip symbol and * debug hunks) * * NOTES * Note that this program does nothing when run from the Workbench * and thus does not have an icon. * * SEE ALSO * "Programming is like sex - * one mistake and you have to support it for life." - Michael Sinz * * BUGS * ******************************************************************************* */ /****** Line number debug information: Looks like it is a debug hunk which look as follows: 0x000003F1 <size in long words> <offset relative to current hunk> 0x4C494E45 'LINE' <number of long words in the filename> <file name, not NULL terminated if exactly the right length> <line number> <mask with 0x00FFFFFF since the upper byte is special> <offset> <line number> <mask with 0x00FFFFFF since the upper byte is special> <offset> ... ******/ #include <exec/types.h> #include <exec/execbase.h> #include <exec/tasks.h> #include <exec/memory.h> #include <exec/alerts.h> #include <string.h> #include <clib/exec_protos.h> #include <pragmas/exec_pragmas.h> #include <clib/dos_protos.h> #include <pragmas/dos_pragmas.h> #include <utility/tagitem.h> #include <clib/utility_protos.h> #include <pragmas/utility_pragmas.h> #include <dos/dosextens.h> #include <dos/datetime.h> #include <dos/dosasl.h> #include <dos/rdargs.h> #define DOSLIB "dos.library" #define DOSVER 37L /******************************************************************************/ #include <exec/ports.h> #include <exec/libraries.h> #include <exec/semaphores.h> #include <exec/resident.h> #include <dos/doshunks.h> #include <dos/dos.h> #include <dos/dosextens.h> #include <dos/stdio.h> #include "findhit_rev.h" /******************************************************************************/ #define CHECKEOF(x) if (((ULONG)&bufferpos[x]) > ((ULONG)bufferend)) { rc=ERROR_FILE_NOT_OBJECT; goto ReadError; } #define GETLONG getLong=*bufferpos++; CHECKEOF(0) #define MASK_LINE (0x00FFFFFF) /* * This command finds the fine/line number of a SgeTracker hit... */ /* This is the command template. */ #define TEMPLATE "FILE/A,OFFSETS/A/M" VERSTAG #define OPT_FILE 0 #define OPT_OFFSETS 1 #define OPT_COUNT 2 LONG cmd(void) { struct Library *SysBase = (*((struct Library **) 4)); struct Library *DOSBase; struct Process *proc; struct Message *msg=NULL; LONG rc=RETURN_FAIL; proc=(struct Process *)FindTask(NULL); if (!(proc->pr_CLI)) { WaitPort(&(proc->pr_MsgPort)); msg=GetMsg(&(proc->pr_MsgPort)); } else if (DOSBase = OpenLibrary(DOSLIB,DOSVER)) { struct RDArgs *rdargs; LONG opts[OPT_COUNT]; memset((char *)opts, 0, sizeof(opts)); rdargs = ReadArgs(TEMPLATE, opts, NULL); if (rdargs == NULL) PrintFault(IoErr(),NULL); else { BPTR fh; if (fh=Open((char *)opts[OPT_FILE],MODE_OLDFILE)) { ULONG getLong; ULONG *buffer; ULONG buffersize; /* * Ok, so now we need to do the hard stuff * for each file... */ rc=RETURN_OK; Seek(fh,0,OFFSET_END); buffersize=Seek(fh,0,OFFSET_BEGINNING); if (buffer=AllocVec(buffersize,MEMF_PUBLIC)) { if (buffersize==Read(fh,buffer,buffersize)) { ULONG hitHunk=0; char **hex; for (hex=(char **)opts[OPT_OFFSETS];(*hex);hex++) { ULONG *bufferpos; ULONG *bufferend; ULONG tableSize; ULONG firstHunk; ULONG lastHunk; ULONG hunk; ULONG hunkType; ULONG hitOffset; ULONG foundOffset; ULONG foundName; ULONG foundLine; char *p; ULONG c; hitOffset=0; p=*hex; if (*p=='$') p++; /* Support $hex */ while (c=(ULONG)*p) { if (c==':') { /* * So this one had a hunk too, so change the hitHunk */ hitHunk=hitOffset; hitOffset=0; p++; if (*p=='$') p++; /* Support $hex */ } else { if ((c>='a') && (c<='f')) c-=32; c-='0'; if (c>9) { c-=7; if (c<10) c=16; } if (c<16) { hitOffset=(hitOffset << 4) + c; p++; } else { hitOffset=0; p=&p[strlen(p)]; } } } /* Now look for the hunk/offset */ foundOffset=0x7FFFFFFF; foundName=0; foundLine=0; bufferpos=buffer; bufferend=(ULONG *)(((ULONG)buffer)+buffersize); GETLONG; /* Check for hunk header... */ if (getLong != HUNK_HEADER) { rc=ERROR_FILE_NOT_OBJECT; goto ReadError; } GETLONG; /* Check for resident lib */ if (getLong != 0) { rc=ERROR_FILE_NOT_OBJECT; goto ReadError; } GETLONG; /* Get table size */ tableSize=getLong; GETLONG; /* Get first hunk slot */ firstHunk=getLong; GETLONG; /* Last hunk slot */ lastHunk=getLong; CHECKEOF(tableSize); bufferpos=&bufferpos[tableSize]; hunk=firstHunk; while ((!rc)&&(hunk<=lastHunk)) { if (bufferpos != bufferend) { GETLONG; hunkType=getLong & 0x3FFFFFFF; switch (hunkType) { case HUNK_DEBUG: if (hunk==hitHunk) { ULONG debugSize; ULONG baseOffset; ULONG checkName; ULONG look=0; debugSize=bufferpos[0]; CHECKEOF(debugSize); baseOffset=bufferpos[1]; if (bufferpos[2]=='LINE') { /* * Name of source file * We play fast and loose * here with the fact that we * will be setting to 0 the upper * byte of the longword after the * file name (for other reasons) and * thus the file name will be NULL * terminated in all cases. */ checkName=(ULONG) &bufferpos[4]; /* Skip source file name, get to lines */ look=4+bufferpos[3]; /* * Kludge to work around a SAS/C 6.50+ strangeness... * We ignore LINE debug stuff with no offset and no * line offsets in the first two places. */ if ((!baseOffset) && (!bufferpos[look+1]) && (!bufferpos[look+3])) look=0; } else if ((bufferpos[2]=='SRC ') && (debugSize>16)) { /* * Silly SAS/C 5.x did some strange stuff * It sometimes used the 'LINE' stuff and sometimes * it used the 'SRC ' stuff... */ checkName=(ULONG) &bufferpos[15]; /* Skip source file name, get to lines */ look=15 + ((bufferpos[14]+3) >> 2); if (look==15) look=0; } if ((look) && (baseOffset<=hitOffset)) { baseOffset=hitOffset-baseOffset; while ((look<debugSize) && (bufferpos[look]&=MASK_LINE)) { ULONG junk=baseOffset-bufferpos[look+1]; if (junk < foundOffset) { foundOffset=junk; foundLine=bufferpos[look]; foundName=checkName; } look+=2; } } } /* Fall down into the skip code */ case HUNK_CODE: case HUNK_DATA: case HUNK_NAME: GETLONG; CHECKEOF(getLong); bufferpos=&bufferpos[getLong]; break; case HUNK_RELOC32: case HUNK_SYMBOL: while (getLong) { GETLONG; if (getLong) { getLong++; CHECKEOF(getLong); bufferpos=&bufferpos[getLong]; } } break; case HUNK_END: hunk++; break; case HUNK_BSS: bufferpos++; CHECKEOF(0); break; default: rc=ERROR_OBJECT_WRONG_TYPE; goto ReadError; } } } if (foundName && foundLine) { ULONG tmp[2]; tmp[0]=foundName; tmp[1]=foundLine; VPrintf("%s : Line %ld\n",(LONG *)tmp); } else { ULONG tmp[2]; tmp[0]=hitHunk; tmp[1]=hitOffset; VPrintf("No line number information for Hunk $%lx, Offset $%lx.\n",(LONG *)tmp); } } } else rc=IoErr(); ReadError: FreeVec(buffer); } else rc=IoErr(); Close(fh); } else rc=IoErr(); if (rc) { PrintFault(rc,NULL); rc=RETURN_FAIL; } FreeArgs(rdargs); } CloseLibrary(DOSBase); } else if (DOSBase=OpenLibrary(DOSLIB,0)) { Write(Output(),"Requires Kickstart 2.04 (37.175) or later.\n",43); CloseLibrary(DOSBase); } if (msg) { Forbid(); ReplyMsg(msg); } return(rc); } | ||