/***************************************************************************** * libtrace.c * -------- * * libtrace allows you to run an executable and trap all calls to external * library functions. Any such trapped calls are printed to a trace file along * with the input and return parameters. * * The library calls to trap and the number and format of the input/output * parameters is defined in a "Trace Definition" file. By default this trace * definition file is called "traceip" and, if not present in the current * working directory, should be located in your PATH. An alternate trace * definition file can be defined with the -T flag. * * The -v (verbose) flag allows attached libraries themselves to be traced. In * other words if a library function calls another library function then this * is traced. Such calls are indented to show the nesting of the function calls. * However, be aware that if a library function calls a function within its * own module then this will NOT be traced. This is due to the way "libtrace" * operates: see "how it works" and "restrictions" below for an explanation. * * To trace an executable just prepend the normal command with "libtrace". * Libtrace will look for the target executable using the PATH just as the * shell would. Trace output will normally be directed to standard out but * a trace output file can be specified using the -o flag. * * If the target process calls "fork" then libtrace will track both the parent * and child processes. A header line is printed for the parent and child * processes. Each line of trace output is prepended with the PID of the * process that created it. * * Signal Handling * --------------- * Calls to the "signal" or "sigaction" functions are trapped automatically * and a pointer to a generic signal handler contained within "libtrace" is * subsitituted. This generic signal handler prints a message to indicate a * signal handler has been invoked before calling the ORIGINAL signal handler. * When the original signal handler returns a message is printed indicating * the end of the signal handler code before the generic signal handler * returns control to the traced program. * * How It Works * ------------ * libtrace operates by manipulating the TOC (Table Of Contents) area of the * target executable and (if -v is specified) any linked library modules. In * order to do this it "loads" the executable into its own address space. The * "load" system call will also load any dependant library modules and resolve * all external dependencies. Upon return the "load" call returns the entry * point of the executable. * * An object file's TOC contains a set of pointers to "Function Descriptors". * If you ask for the address of a function in 'C' you will actually get the * address of the Function Descriptor. A function descriptor contains three * pointers - a pointer to the code, a pointer to the TOC of the code (which * may well be different if the code is in a library module) and an environment * pointer (which is not used in 'C' programs but is hijacked by "libtrace" for * its own purposes - see below). * * RS/6000 (PowerPC) compilers generate either ".glink" (Global Linkage) or * ".ptgrl" (Pointer Global Linkage) code for all external library * functions. This code retrieves the address of the function and its * associated TOC using the pointer stored in the TOC of the target. Thus, if * one knows the address of the traced function it is relatively simple to * find the function pointer in the TOC and change it to point elsewhere. * "libtrace" uses this technique to point all traced functions to the * "GenericFunction" contained here. * * This technique is illustrated below: * * BEFORE: * * TOC of target application * ------------------------- * ptr1 "printf" ------------> | ptr to printf code | ptr to TOC | env * ptr2 "ioctl" ------------> | ptr to ioctl code | ptr to TOC | env * * AFTER: * * TOC of target application * ------------------------- * ptr1 "printf" ------------> | ptr to GenericFunction code | ptr to TOC | env * ptr2 "ioctl" ------------> | ptr to GenericFunction code | ptr to TOC | env * * Note that although "ptr1" and "ptr2" seem to point to the same structure, * "libtrace" actually "mallocs" a new Function Descriptor for each traced * function. This is to allow "GenericFunction" to determine from which * traced function it was called as follows: * * When "GenericFunction" is called it obviously needs to ascertain what the * "original" function was. It does this by scanning a global linked list that * contains all the traced functions along with a pointer to their new function * descriptor. These new function descriptors are identical (since they all * point to "GenericFunction") but they are all "malloced" so that they occupy * unique addresses. * * In order to search this linked list we need to establish the original * pointer to the function descriptor as derived by either the ".glink" or * ".ptgrl" compiler-generated code. This is easy for ".glink" code since it * is left in general purpose register 12 (r12). However, ".ptgrl" code does * not. Indeed, it will normally lose any reference to the original function * descriptor pointer. Assembler listings of both .ptgrl and .glink code are * shown in the comment header of the libtrace_asm.s file. * * To work around this, when we create a "new" function descriptor pointer we * save its address in the "environment" pointer section. This part of the * function descriptor is not used in 'C' programs and is conventionally zero. * The ".ptgrl" code leaves the contents of this environment pointer in * general purpose register 11 (r11). * * The assembler code module "libtrace_asm.s" contains the function * "GetDescriptorAddr" which ascertains the calling "glue" function (.glink or * .ptgrl) and returns the appropriate function descriptor pointer as contained * in either r11 (for .ptgrl) or r12 (for .glink). * * When the address of the function descriptor pointer has been established * it is a relatively simple matter to scan the linked list looking for this * pointer. When it is found, this is the original function details. This is * then used to print the source parameters, call the original function and * finally print the return value. * * Finding the address of a "normal" library function is relatively simple. * The loader section of the target contains values which, if added to the * start of the data section of the target, gives a pointer to the function * descriptor. * * Functions calls contained in the kernel are a little more tricky. Such calls * (open, lseek, socket etc) are defined in libc.a for linking purposes but * are defined as both "imported" and "exported" and are not resolved in the * loader section of the target. These calls (known as SuperVisor calls) are * identified by the keyword "syscall" in *.exp files in /usr/lib dir. * * In order to trap these calls we have to use "ld" library functions to scan * the library modules and recover the "value" for the function. When added to * the start of the data area of the target this gives a pointer to the * entry in the TOC. Thus, if this pointer is indirected we get the same type * of entry as defined for a normal library function. * * Restrictions: * ------------- * Because libtrace manipulates the TOC in order to trap EXTERNAL function * calls it is not possible to trace library functions which are called from * functions in the same module. This is because such calls are made using * "Branch with Link" (bl) calls and not via ".glink" or ".ptgrl" generated * code. For example the function "catgets" calls "fopen" which calls the * kernel function "open". However, "libtrace" output will show something like: * * 22516) catgets(0x20021068,1,9,"flags") * 22516) open("/usr/lib/nls/msg/en_US/ifconfig.cat",0) returns 4 * . * . * 22516) returns 0x200210f8 * * ...since "fopen" and "catgets" both exist within "shr.o" within "libc.a". * Thus, when "catgets" calls "fopen" it uses a "bl" command to call "fopen" * directly (since it is within the same local module). When "fopen" calls * "open" it uses .glink code (since it is effectively external in the kernel) * and therefore this is trapped and traced. * * Also be aware that (obviously) set-UID programs will not run set-UID when * under control of "libtrace" and therefore will not behave as expected * unless they are run as the target user. A warning is issued to this effect * to standard error and the trace file whenever a set-uid/set-gid program is * traced. * * IMPORTANT * --------- * Due to the way parameter passing is implemented in PowerPC architectures, * floating point parameters and functions with more than 8 parameters cannot * be traced. This may cause problems, especially with printf/sprintf/fprintf * calls which may pass floating point values for output. Such calls will * probably result in segmentation violations or other unfortunate exits. In * such circumstances it will be necessary to comment out the offending call * within "traceip" which will prevent the problematic interception. * * BIBLIOGRAPHY: * ------------- * i) man a.out (!!) * ii) "Understanding and Programming the TOC" (info pages) * iii) "Optimizing PowerPC Code" - Gary Kacmarcik (ISBN 0-201-40839-2) * * WARRANTY & CAVEATS * ------------------ * Any program that tries to do what "libtrace" does is highly unlikely to be * bug-free. Unsurprisingly, "libtrace" has no warranty (including any implied * warranty of merchantablity or fitness for a particular purpose). The software * is being made available free of charge in the hope that AIX users or * administrators may find it useful but all responsibility as to its use are * to be assumed by the user. Be aware that some applications may have licence * conditions forbidding reverse-engineering and that use of "libtrace" on such * applications may breach the terms of your licence with the application * vendor. * * * Compilation instructions: * ------------------------- * Use supplied makefile (make -f makefile.libtrace) or compile with: * cc libtrace.c libtrace_asm.s -lld [-g] -o libtrace * * Miscellaneous: * -------------- * Author: Phil Gibbs - Trinem Consulting (pgibbs@trinem.co.uk) * Date: 14th Dec 1999 * *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /***************************************************************************** * Global variables for option flags. *****************************************************************************/ extern int optind; extern int Optopt; extern int Opterr; extern char *optarg; /***************************************************************************** * Global "TraceFile" variable points to standard out by default or can be * redirected to a named file by use of the -o option. * "TraceName" is a character array holding the trace filename specified * with the -o option. If the -p (append pid) option is specified then the pid * is appended to this filename. * "AppendPid" flag adds the pid of the traced process to the trace filename. * Useful if traced process forks when separate trace files can be created for * child processes. *****************************************************************************/ char TraceName[256]; FILE *TraceFile; short AppendPid; /***************************************************************************** * TraceOn flag is set just before the traced program is entered. * "GenericFunction" will not print any trace output unless this flag is set. * The reason for this is to allow "libtrace" to make library calls after the * TOC of libc.a has been manipulated (-v option) without such calls being * traced themselves! *****************************************************************************/ short TraceOn=0; /***************************************************************************** * EXTERNAL structure is a linked-list element which contains the library * functions to be traced, a pointer to the new function descriptor (which is * used to determine the original function being called) a pointer to the * original function to call and the input and return parameter formats for * the trace. An optional "FailValue" contents the fail return code which * causes errno to be printed as well. *****************************************************************************/ typedef struct ext_tag{ char *Name; char *Address; char *FunctionDescriptor; char *OrigFunctionPtr; char *ParameterFormat; char *ReturnFormat; char *FailValue; struct ext_tag *next; } EXTERNAL; /***************************************************************************** * SYMBOL_TABLE structure is determined from the library archive file(s). * Contains the symbol name and the address in the data area of the * relevant function descriptor. *****************************************************************************/ typedef struct SymbolTableTag{ char *Symbol; char *Value; struct SymbolTableTag *next; } SYMBOL_TABLE; /***************************************************************************** * EXTERNAL StartLib is the head of the linked list of traced functions. *****************************************************************************/ EXTERNAL *StartLib=(EXTERNAL *)0; /***************************************************************************** * typedefs for FUNCTION_PTR (entry point of traced program) * SIGNAL_HANDLER (any trapped signal handler function) *****************************************************************************/ typedef int FUNCTION_PTR(int,char **,char **); typedef void SIGNAL_HANDLER(int); /***************************************************************************** * array of SIGNAL_HANDLER function pointers - one per trapped signal *****************************************************************************/ SIGNAL_HANDLER *SignalHandler[128]; /***************************************************************************** * Function - name of function specified with -a (print address). This is * an undocumented feature of libtrace used for debugging purposes. *****************************************************************************/ char Function[256]; /***************************************************************************** * ExitWithError * ------------- * Writes format with variable argument list to stderr and exits. *****************************************************************************/ typedef enum perr_tag{NO_PERROR,DO_PERROR} PERROR; void ExitWithError(PERROR do_perror,char *fmt, ...) { va_list args; TraceOn=0; /* Prevent lib calls tracing */ va_start(args,fmt); vfprintf(stderr,fmt,args); va_end(args); if (do_perror==DO_PERROR) perror(" "); exit(errno); } /***************************************************************************** * Malloc * ------ * Local version of malloc with checks for zero return codes. *****************************************************************************/ void *Malloc(size_t size) { void *res; res=malloc(size); if (res==(void *)0) { perror("malloc"); exit(1); } } /***************************************************************************** * GetTraceFileName. * ----------------- * Returns the contents of the global "TraceName" variable. If the "AppendPid" * flag is set then adds the current process pid. Used after "fork" calls to * ensure child processes gain their own trace file. *****************************************************************************/ char *GetTraceFileName() { char *res; if (AppendPid) { res=(char *)Malloc(strlen(TraceName)+10); strcpy(res,TraceName); sprintf(&(res[strlen(res)]),".%d",getpid()); } else { res=TraceName; } return res; } /***************************************************************************** * FindFunctionFromDescriptor * -------------------------- * Scans the linked list of traced functions looking for the given * Function Descriptor pointer. When located, a pointer to the required * EXTERNAL structure is returned. Failure to find the required pointer * within the linked list is a fatal error causing "libtrace" to exit. *****************************************************************************/ EXTERNAL *FindFunctionFromDescriptor(char *DescriptorPointer) { EXTERNAL *CurrLib; CurrLib=StartLib; while (CurrLib) { if (CurrLib->FunctionDescriptor==DescriptorPointer) { return CurrLib; } CurrLib=CurrLib->next; } ExitWithError( NO_PERROR, "\n\nCould not find orig lib function from offset 0x%lx\n\n", DescriptorPointer); } /***************************************************************************** * DepthString * ----------- * Returns a character string of a variable amount of spaces depending on * the passed "CallDepth". Used for indenting on trace file output. *****************************************************************************/ char *DepthString(short CallDepth) { static char Result[200]; short k; for (k=0;k>>",getpid(),signo); OrigSignalHandler=SignalHandler[signo]; OrigSignalHandler(signo); fprintf(TraceFile,"\n%d) <<< SIGNAL HANDLER EXITS >>>",getpid()); } /***************************************************************************** * GenericFunction * --------------- * Called for every traced function. Determines the original function being * traced by scanning the EXTERNAL linked list, looking for a function * descriptor pointer as derived from "GetDescriptorAddr". Prints the * parameters using the appropriate format string, increments the indent count * and calls the original function. On return, prints the return value using * the appropriate format and decrements the indent count. * * "fork" calls are explicitly trapped and additional trace information * printed. * * "signal" calls are explicitly trapped, the original function descriptor is * recorded and substituted for that for the "GenericSignalHandler". * * "exit" calls are trapped for file flushing purposes. * * Note, the use of 8 generic pointers may cause problems with routines passed * floating point numbers as these will be passed in different registers. Also, * routines with more than 8 parameters have their additional params passed in * a memory block. It would be very difficult to code "GenericFunction" to * work transparently for all such occurences. * *****************************************************************************/ void *GenericFunction( void *r3,void *r4, void *r5,void *r6, void *r7,void *r8, void *r9,void *r10) { long DescAddr; EXTERNAL *LibDetails; typedef char *CODEPTR(void *,void *,void *,void *,void *, void *,void *,void *); CODEPTR *f; char *res; char OutputLine[256]; struct sigaction *SigAction; static short CallDepth=0; static short Nested=0; /* * First we calculate which function this is supposed to be. * Call assembler routine "GetDescriptorAddr" to return the * pointer to the "new" function descriptor that the glue code * (either .ptgrl or .glink) used. */ DescAddr=GetDescriptorAddr(); if (DescAddr==-1) { ExitWithError(NO_PERROR,"\n\nCould not establish glue code type\n"); } LibDetails=FindFunctionFromDescriptor((char *)DescAddr); f=(CODEPTR *)LibDetails->OrigFunctionPtr; if (TraceOn==0) { /* * We are not yet tracing the target. This must mean that * "libtrace" has called a library function within a module * that has had its TOC manipulated (-v option). Just call * the "real" function and exit. */ return f(r3,r4,r5,r6,r7,r8,r9,r10); } if (f!=(CODEPTR *)&fork) { /* * Not a call to "fork". Just print the input parameters. */ fprintf(TraceFile,"\n%d) %s",getpid(),DepthString(CallDepth)); sprintf(OutputLine, LibDetails->ParameterFormat,r3,r4,r5,r6,r7,r8,r9,r10); PrintWithNoLF(OutputLine); fflush(TraceFile); } else { /* This is a call to "fork". Do not print the call here since * we want to differentiate between child and parents processes. * Instead, throw a blank line to the trace file output * (before we print "parent" and "child" headers) and then * flush the output so the child process does not * duplicate-flush the same buffered lines as the parent. */ fprintf(TraceFile,"\n"); fflush(TraceFile); } CallDepth++; Nested=0; if (f==(CODEPTR *)_exit) { fprintf(TraceFile,"\n"); fflush(TraceFile); } if (f==(CODEPTR *)&sigaction) { /* * Code is calling either "signal" or "sigaction" (signal calls * sigaction) */ SigAction=(struct sigaction *)r4; if (SigAction->sa_handler!=SIG_DFL && SigAction->sa_handler!=SIG_IGN) { /* * Application is defining a signal handler. * Record the original signal handler... */ SignalHandler[(int)r3]=SigAction->sa_handler; /* * ...and then substitute our own! */ SigAction->sa_handler=(SIGNAL_HANDLER *)GenericSignalHandler; } } /************************************************* * CALL ORIGINAL FUNCTION ************************************************/ res=f(r3,r4,r5,r6,r7,r8,r9,r10); /************************************************* * END CALL ORIGINAL FUNCTION ************************************************/ if (f==(CODEPTR *)&fork) { /* This is the reply from a fork call. Now we can print the * appropriate header along with the parameters to the call. */ if (AppendPid && res==0) { fclose(TraceFile); TraceFile=fopen(GetTraceFileName(),"w"); fprintf(TraceFile,"Process created due to fork()\n"); } fprintf(TraceFile,"\n%d) ------- Fork Branch (%s) --------", getpid(), res?"Parent":"Child"); fprintf(TraceFile,"\n%d) %s",getpid(),DepthString(CallDepth-1)); fprintf(TraceFile, LibDetails->ParameterFormat,r3,r4,r5,r6,r7,r8,r9,r10); } CallDepth--; /* * Print the return value from the original function call. This should * be on a new line if this function had nested traced functions (-v * option) or at the end of the call line if not. */ if (Nested) { fprintf(TraceFile,"\n%d) %sreturns ", getpid(),DepthString(CallDepth)); } else fprintf(TraceFile," returns "); fprintf(TraceFile, LibDetails->ReturnFormat, res,r3,r4,r5,r6,r7,r8,r9,r10); /* * Print the value of "errno" if the function call has failed. */ if (res==LibDetails->FailValue) { fprintf(TraceFile," (errno=%d)",errno); } Nested=1; fflush(TraceFile); return res; } /***************************************************************************** * GetValueFromSymbolTable * ----------------------- * Scans the given linked list of SYMBOL_TABLE entries, looking for an entry * that matches symbol "Name". When found, returns the matching value. *****************************************************************************/ char *GetValueFromSymbolTable(SYMBOL_TABLE *Start,char *Name) { SYMBOL_TABLE *Curr; char *Reply=(char *)0; Curr=Start; while (Curr) { if (strcmp(Curr->Symbol,Name)==0) { Reply=Curr->Value; break; } Curr=Curr->next; } return Reply; } /***************************************************************************** * FindSymbolsInLoaderSection * -------------------------- * Scans the .loader section of the referenced loaded XCOFF object, finding * all referenced symbols and loading them into the global EXTERNAL linked * list. Unresolved symbols are located in the linked list pointed to by * SYMBOL_TABLE (this list will have been loaded by scanning the appropriate * library files). *****************************************************************************/ void FindSymbolsInLoaderSection( char *buf, SCNHDR *Sections, LDHDR *Loader, LDSYM *LoadSym, void *DataStart, EXTERNAL **StartLibPtr, EXTERNAL **CurrLibPtr, SYMBOL_TABLE *SymbolTable) { int Sym; long Offset; char *SymName; char *StringTable; char *FileTable; char *SectionName; EXTERNAL *CurrLib; EXTERNAL *StartLib; char Buf[200]; int res; char *SymbolValue; StartLib=*StartLibPtr; CurrLib=*CurrLibPtr; StringTable=(char *)((char *)Loader+Loader->l_stoff); FileTable=(char *)((char *)Loader+Loader->l_impoff); for (Sym=0;Syml_nsyms;Sym++) { SymName=LoadSym[Sym].l_name; if (SymName[0]=='\0') { Offset=LoadSym[Sym].l_offset; SymName=&(StringTable[Offset]); } SymbolValue=0; if (LoadSym[Sym].l_ifile==0) { /* Not an external (unresolved) symbol */ SymbolValue=LoadSym[Sym].l_value + DataStart; } else if (LoadSym[Sym].l_smclas==XMC_SV) { /* Unresolved (kernel) symbol. Try and * resolve from exported symbol table. */ SymbolValue=(char *) GetValueFromSymbolTable(SymbolTable,SymName); } if (SymbolValue) { SectionName=(LoadSym[Sym].l_scnum)? Sections[LoadSym[Sym].l_scnum-1].s_name: "external"; if (!StartLib) { StartLib=(EXTERNAL *)Malloc(sizeof(EXTERNAL)); CurrLib=StartLib; } else { CurrLib->next=(EXTERNAL *)Malloc(sizeof(EXTERNAL)); CurrLib=CurrLib->next; } CurrLib->next=(EXTERNAL *)0; CurrLib->Name=SymName; CurrLib->FunctionDescriptor=0; CurrLib->Address=SymbolValue; } } *StartLibPtr=StartLib; *CurrLibPtr=CurrLib; } /***************************************************************************** * FindLoaderSection * ----------------- * Locates the .loader section in the given XCOFF loaded object. *****************************************************************************/ ulong FindLoaderSection(short NumberOfSections,SCNHDR *SecData) { short Section; ulong LoaderSectionOffset; for (Section=0;Sectiono_toc != 0xffffffff)?(AuxHdr->o_toc - AuxHdr->data_start):0; } /***************************************************************************** * Find * ---- * Scans the EXTERNAL linked list (starting at LibStart) looking for the * specified "FunctionName". When located returns a pointer to the appropriate * EXTERNAL structure. *****************************************************************************/ EXTERNAL *Find(EXTERNAL *LibStart,char *FunctionName) { EXTERNAL *LibCurr; LibCurr=LibStart; while (LibCurr) { if (strcmp(LibCurr->Name,FunctionName)==0) { /* Found it */ break; } LibCurr=LibCurr->next; } return LibCurr; } /***************************************************************************** * CreateNewFunctionDescriptor * --------------------------- * Creates and returns a pointer to a new function descriptor. * Function descriptors are in the following format: * * -------------------------------------------------------------- * | Address of Function | Address of TOC | Environment Pointer | * -------------------------------------------------------------- * * The new function descriptor is then populated with the values appropriate * to "GenericFunction". A pointer to this function descriptor is stored in * "PTRfunc" and the pointer is returned. Note "Environment Pointer" is set to * the address of the function descriptor itself. This is so that .ptgrl code * leaves its address in General Purpose Register 11. See libtrace_asm.s source * file for details. * *****************************************************************************/ char *CreateNewFunctionDescriptor(EXTERNAL *PTRfunc,char **k) { char **gf; char **fp; /* * If PTRfunc has already been set up then we have seen a reference * to this library function before. This could happen if the target * application has multiple libraries which link together and the * user has asked to trap ALL library calls (i.e.: including those * from other libraries). Just return the existing pointer to avoid * duplicating lots of unnecessary (idential) function descriptors * and causing search headaches. */ if (PTRfunc->FunctionDescriptor) { return PTRfunc->FunctionDescriptor; } /* * This is a new tracable function so allocate space for the function * descriptor. */ gf=(char **)Malloc(sizeof(void *)*3); /* * Set up the function descriptor. Note that when referring to the * address of a function the compiler always gives the address of the * function descriptor and not the start address of the function. The * function descriptor contains three pointers: * * -------------------------------------------------------------- * | Address of Function | Address of TOC | Environment Pointer | * -------------------------------------------------------------- * * Note the "Environment Pointer" is not used within 'C' code. We * will set it to point to the address of the function descriptor * iself in order to trick ".ptgrl" glue code into leaving the * address of the function descriptor in register 11. */ fp=(char **)GenericFunction; /* addr of function descriptor */ gf[0]=fp[0]; /* ptr to function */ gf[1]=fp[1]; /* ptr to TOC */ gf[2]=(char *)gf; /* ptr to self for GenericFunction */ /* * Now record the address of this function descriptor. When * "GenericFunction" is called, the address of this function descriptor * can be calculated with the "GetDescriptorAddr" function and we can * use this to calculate what the REAL function is that is being called. */ PTRfunc->FunctionDescriptor=(char *)gf; /* * Record the address of the ORIGINAL function descriptor. We will use * this to call the REAL function after we trace the call. */ PTRfunc->OrigFunctionPtr=*k; /* * Now return the pointer to the new function descriptor. */ return (char *)gf; } /***************************************************************************** * ModifyToc * --------- * Scans the Table Of Contents in the .data section of the specified XCOFF * object, looking for functions defined in the trace file and substituting * and recording pointers to the replacement "GenericFunction" for all such * located functions. *****************************************************************************/ void ModifyToc(char *TraceDefnName,char *TocStart,char *TocEnd,EXTERNAL *LibStart) { char **k; EXTERNAL *LibCurr; EXTERNAL *PTRfunc; char *LocalToc; FILE *ip; char *LibFunction; char *ParemFormat; char *ReturnFormat; char *FailValue; char Buffer[2048]; char *pf,*rf; char *Comment; ip=fopen(TraceDefnName,"r"); if (!ip) { ExitWithError( DO_PERROR, "Couldn't open trace defn file \"%s\"", TraceDefnName); } while (!feof(ip)) { fgets(Buffer,sizeof(Buffer),ip); Comment=(char *)strchr(Buffer,'#'); if (Comment) *Comment='\0'; LibFunction=(char *)strtok(Buffer,"|"); ParemFormat=(char *)strtok((char *)0,"|"); ReturnFormat=(char *)strtok((char *)0,"|\n"); FailValue=(char *)strtok((char *)0,"|\n"); PTRfunc=Find(LibStart,LibFunction); /* * Following code tripped by -a flag (undocumented debug feature!) */ if (LibFunction[0] && strcmp(LibFunction,Function)==0) { if (PTRfunc) printf("%s is at 0x%lx\n",Function,PTRfunc->Address); else printf("address of %s could not be establised\n"); } k=(char **)TocStart; while((char *)k<=TocEnd && PTRfunc) { if (*k==PTRfunc->Address) { *k=CreateNewFunctionDescriptor(PTRfunc,k); /* * Now take a copy of the parameter and return * format strings and record them against this * entry. This is so "GenericFunction" can * print the appropriate input and return * parameters to the function. */ pf=(char *)Malloc(strlen(ParemFormat)+1); strcpy(pf,ParemFormat); PTRfunc->ParameterFormat=pf; rf=(char *)Malloc(strlen(ReturnFormat)+1); strcpy(rf,ReturnFormat); PTRfunc->ReturnFormat=rf; if (FailValue[0]) { PTRfunc->FailValue= (char *)atol(FailValue); } else { PTRfunc->FailValue=(char *)0xdeadbeef; } } k++; } } fclose(ip); } /***************************************************************************** * AddSymbol * --------- * Adds the given symbol to the linked list defined by the "Start" and "Curr" * entries. Called to set up SYMBOL_TABLE linked list which contains external * functions defined in the appropriate libraries. *****************************************************************************/ void AddSymbol( SYMBOL_TABLE **Start, SYMBOL_TABLE **Curr, LDFILE *ldPointer, SYMENT *Symbol, char *DataStart) { char *SymName; char **temp; char *SymbolValue; if (!(*Start)) { *Start=(SYMBOL_TABLE *)Malloc(sizeof(SYMBOL_TABLE)); *Curr=*Start; } else { (*Curr)->next=(SYMBOL_TABLE *)Malloc(sizeof(SYMBOL_TABLE)); (*Curr)=(*Curr)->next; } SymName=(char *)ldgetname(ldPointer,Symbol); (*Curr)->Symbol=(char *)Malloc(strlen(SymName)+1); strcpy((*Curr)->Symbol,SymName); (*Curr)->next=(SYMBOL_TABLE *)0; temp=(char **)(Symbol->n_value+DataStart); SymbolValue=(char *)*temp; (*Curr)->Value=SymbolValue; } /***************************************************************************** * LoadSymbolForMember * ------------------- * Loads the symbols for the archive member refered to by "ldPointer". Used * to populate the SYMBOL_TABLE linked list referred to via "Start" and "Curr". *****************************************************************************/ void LoadSymbolForMember( LDFILE *ldPointer, SYMBOL_TABLE **Start, SYMBOL_TABLE **Curr, char *DataStart ) { int x; SYMENT Sym; if (ldtbseek(ldPointer)!=FAILURE) { x=0; while (ldtbread(ldPointer,x++,&Sym)!=FAILURE) { if ( Sym.n_scnum==2 && Sym.n_sclass==C_HIDEXT && strcmp(Sym.n_name,"_$STATIC") ) { AddSymbol(Start,Curr,ldPointer,&Sym,DataStart); } } } } /***************************************************************************** * LoadSymbolTable * --------------- * Scans the member "Member" within the archive library file "Archive" and * calls LoadSymbolForMember in order to load all required symbols into the * SYMBOL_TABLE linked list identified via the "Start" and "Curr" parameters. *****************************************************************************/ void LoadSymbolTable( char *Archive, char *Member, SYMBOL_TABLE **Start, SYMBOL_TABLE **Curr, char *DataStart) { /* * Note: 4.3 onwards uses a different archive file format. This function * therefore will need to pick up the relevant header files. In other * words it will be required to compile this routine on a 4.3 box. */ LDFILE *ldPointer; ARCHDR ArchiveHeader; ldPointer = NULL; do { if((ldPointer = ldopen(Archive, ldPointer)) != NULL) { ldahread(ldPointer,&ArchiveHeader); if (strcmp(ArchiveHeader.ar_name,Member)==0) LoadSymbolForMember(ldPointer,Start,Curr,DataStart); } } while(ldclose(ldPointer) == FAILURE ); } /***************************************************************************** * ControlExecutable * ----------------- * Main control routine. *****************************************************************************/ void ControlExecutable( char *Buffer, FUNCTION_PTR *EntryPoint, short RestrictedFlag, char *TraceDefnName, int argc, char **argv, char **env) { char **k; struct ld_info *ld; struct ld_info *ControlProg; int CurrentOffset; int LastOffset; char *ElementName; ulong Toc=0; ulong LoaderSectionOffset; SYMBOL_TABLE *SymbolTable=(SYMBOL_TABLE *)0; SYMBOL_TABLE *CurrPtr=(SYMBOL_TABLE *)0; EXTERNAL *CurrLib; char LibMember[256]; ld=(struct ld_info *)Buffer; CurrentOffset=0; LastOffset=-1; while (CurrentOffset > LastOffset) { LoaderSectionOffset=FindLoaderSection( ((FILHDR *)(ld->ldinfo_textorg))->f_nscns, (SCNHDR *)((char *)ld->ldinfo_textorg+ sizeof(FILHDR)+sizeof(AOUTHDR)) ); ElementName=ld->ldinfo_filename+strlen(ld->ldinfo_filename)+1; if (LastOffset==-1) { fprintf(TraceFile,"Object Name Text Start Text End Data Start Data End\n"); fprintf(TraceFile,"--------------------------- ---------- ---------- ---------- ----------\n"); } else { strcpy(LibMember,ld->ldinfo_filename); if (strlen(ElementName)) { strcat(LibMember,":"); strcat(LibMember,ElementName); } fprintf(TraceFile,"%-30s 0x%8lx 0x%8lx 0x%8lx 0x%8lx\n", LibMember, ld->ldinfo_textorg, ld->ldinfo_textorg+ld->ldinfo_textsize, ld->ldinfo_dataorg, ld->ldinfo_dataorg+ld->ldinfo_datasize ); } if (strlen(ElementName)) { /* Must be an archive (library) file. Load the * Symbols. */ LoadSymbolTable(ld->ldinfo_filename, ElementName, &SymbolTable, &CurrPtr, ld->ldinfo_dataorg); } if ( (void *)EntryPoint>=ld->ldinfo_dataorg && (void *)EntryPoint<=(ld->ldinfo_dataorg+ld->ldinfo_datasize)) { if (!Toc) Toc=TocOffset((char *)ld->ldinfo_textorg); else { /* * Should never, ever happen but just in case... */ ExitWithError( NO_PERROR, "Entry Point lies in multiple modules"); } ControlProg=ld; } else { FindSymbolsInLoaderSection( (char *)ld->ldinfo_textorg, (SCNHDR *)((char *)ld->ldinfo_textorg+ sizeof(FILHDR)+sizeof(AOUTHDR)), (LDHDR *)((char *)ld->ldinfo_textorg+ LoaderSectionOffset), (LDSYM *)((char *)ld->ldinfo_textorg+ LoaderSectionOffset+sizeof(LDHDR)), ld->ldinfo_dataorg, &StartLib, &CurrLib, SymbolTable ); } LastOffset=CurrentOffset; CurrentOffset=CurrentOffset+ld->ldinfo_next; ld=(struct ld_info *)&(Buffer[CurrentOffset]); } if (RestrictedFlag==0) { /* * Only do this bit if we are going to modify the TOCs for all * linked modules as well. */ ld=(struct ld_info *)Buffer; CurrentOffset=ld->ldinfo_next; ld=(struct ld_info *)&(Buffer[CurrentOffset]); LastOffset=-1; while (CurrentOffset > LastOffset) { Toc=TocOffset((char *)ld->ldinfo_textorg); if (Toc) ModifyToc( TraceDefnName, Toc+ld->ldinfo_dataorg, ld->ldinfo_dataorg+ld->ldinfo_datasize, StartLib); LastOffset=CurrentOffset; CurrentOffset=CurrentOffset+ld->ldinfo_next; ld=(struct ld_info *)&(Buffer[CurrentOffset]); } } else { /* * Do this bit if we are just tracing the specified program. */ if (Toc) { ModifyToc( TraceDefnName, Toc+ControlProg->ldinfo_dataorg, ControlProg->ldinfo_dataorg+ ControlProg->ldinfo_datasize, StartLib); } else { ExitWithError(NO_PERROR,"Could not find TOC for %s\n",argv[0]); } } /* Reset "optind" and "optarg" global variables in case the program * we are tracing reads command-line switches. */ optind=1; optarg=(char *)0; fprintf(TraceFile,"\nTrace begins on pid %d:\n",getpid()); /* * Note: "SetRegisters" call is required on 4.2.1 and onwards. It * simply sets r30 and r31 to the same value. If they differ, the * entry code calls "__modinit" which promptly segment violates. Not * sure what the significance of r30 and r31 are but making them the * same avoids the problem. */ SetRegisters(); /* * Jump to program entry point. Calls to traced functions will be * routed through "GenericFunction". */ TraceOn=1; EntryPoint(argc,argv,env); } /***************************************************************************** * GetFullPathOfTarget * ------------------- * Scans the PATH looking for the specified target. When found returns the * full path of the located target. If not found in the PATH returns the * passed parameter prepended with "./" *****************************************************************************/ char *GetFullPathOfTarget(char *Target) { char *Path; char *CurrPoint; char *Dir; char *OrigPath; char *TargetPath; int DirLength; char *Reply=(char *)0; struct stat Details; short FullPathSpecified; FullPathSpecified=( Target[0]=='/' || (Target[0]=='.' && Target[1]=='/') || (Target[0]=='.' && Target[1]=='.' && Target[2]=='/')); if (!FullPathSpecified) { OrigPath=getenv("PATH"); if (!OrigPath) ExitWithError(NO_PERROR,"Could not establish PATH\n"); Path=(char *)Malloc(strlen(OrigPath)+1); strcpy(Path,OrigPath); CurrPoint=Path; while (Dir=(char *)strtok(CurrPoint,":")) { CurrPoint=(char *)0; DirLength=strlen(Dir); TargetPath=(char *)Malloc(DirLength+1+strlen(Target)+1); sprintf(TargetPath,"%s/%s",Dir,Target); if (stat(TargetPath,&Details)==0) { /* Target located in path - regular file? */ if (Details.st_mode & _S_IFREG) { Reply=TargetPath; break; } } else { free(TargetPath); } } free(Path); if (!Reply) { TargetPath=(char *)Malloc(strlen(Target)+3); sprintf(TargetPath,"./%s",Target); Reply=TargetPath; } } else { Reply=Target; } return Reply; } typedef enum UserGroupTag{UID,GID} USER_OR_GROUP; void IssueWarning(char *FullPath,int Id,USER_OR_GROUP Type) { char *WarningText; struct passwd *User; struct group *Group; char *TypeText; char *Owner; WarningText= "WARNING: %s is NOT running set-%s \"%s\":" " Behaviour may be unexpected\n\n"; User=getpwuid(Id); Group=getgrgid(Id); switch(Type) { case UID: TypeText="uid"; Owner=User->pw_name; break; case GID: TypeText="gid"; Owner=Group->gr_name; break; } fprintf(stderr,WarningText,FullPath,TypeText,Owner); if (TraceFile!=stdout) { fprintf(TraceFile,WarningText,FullPath,TypeText,Owner); } } /***************************************************************************** * * ENTRY POINT * *****************************************************************************/ void main(int argc,char **argv,char **env) { int *res; FUNCTION_PTR *EntryPoint; struct stat TraceIpStat; char Buffer[20000]; int opt; int k; char *FullPath; short RestrictedFlag=1; char **ErrorPtr; struct stat Details; char TraceDefnName[1024]; char *TraceDefnPtr; char *Usage="USAGE: %s [-v][-o tracefile [-p]] [-T] Program \n"; AppendPid=0; TraceFile=stdout; /* Default file handle for trace output */ /* * Find "traceip" trace definition file. Look in current directory * first. If not there, use PATH. */ TraceDefnPtr="traceip"; if (stat(TraceDefnPtr,&TraceIpStat)==-1) { if (errno==ENOENT) { /* Not located in cwd - use path. */ TraceDefnPtr=GetFullPathOfTarget(TraceDefnPtr); } else { perror("stat"); exit(1); } } TraceDefnName[0]='\0'; while ((opt=getopt(argc,argv,":po:vT:a:")) != EOF) { switch (opt) { case 'o': strncpy(TraceName,optarg,sizeof(TraceName)); TraceName[sizeof(TraceName)-1]='\0'; TraceFile=fopen(GetTraceFileName(),"w"); if (!TraceFile) { ExitWithError(NO_PERROR, "Can't open trace output file %s\n", optarg); } break; case 'T': strncpy(TraceDefnName,optarg, sizeof(TraceDefnName)); TraceDefnName[sizeof(TraceDefnName)-1]='\0'; break; case 'v': /* verbose flag - descend into * library heirarchies. */ RestrictedFlag=0; break; case 'a': /* Get address of function (undocumented debug) */ strncpy(Function,optarg,sizeof(Function)-1); break; case 'p': /* Append pid to trace filename */ AppendPid=1; break; default: ExitWithError(NO_PERROR,Usage,argv[0]); } } if (AppendPid && TraceName[0]=='\0') { /* Cannot specify -p without -o */ ExitWithError(NO_PERROR,Usage,argv[0]); } if (strlen(argv[optind])) { FullPath=GetFullPathOfTarget(argv[optind]); } else { ExitWithError(NO_PERROR,Usage,argv[0]); } if (TraceDefnName[0]=='\0') { /* No trace definition file specified with -T. Use located * "traceip". */ if (TraceDefnPtr) { strncpy(TraceDefnName,TraceDefnPtr, sizeof(TraceDefnName)); } else ExitWithError(NO_PERROR,"Cannot locate \"traceip\" definition file\n"); } if (stat(TraceDefnName,&TraceIpStat)==0) { /* Trace definition file (either default or user-specified) * can be seen okay. */ } else /* Failed to locate trace definition file. */ ExitWithError(DO_PERROR,"Cannot open trace definition file \"%s\"", TraceDefnName); if (stat(FullPath,&Details)==0) { /* File exists */ res=(int *)load(FullPath,L_LIBPATH_EXEC,0); } else { ExitWithError(DO_PERROR,"Cannot load %s",FullPath); } if (res) { /* File was loaded successfully */ EntryPoint=(FUNCTION_PTR *)res; if (loadquery(L_GETINFO,(void *)Buffer,sizeof(Buffer))!=-1) { /* We've queried the load data ok */ fprintf(TraceFile,"Tracing library calls from: %s ",FullPath); for (k=optind+1;k<=argc;k++) { fprintf(TraceFile,"%s ",argv[k]); } fprintf(TraceFile,"\n\n"); /* Issue warnings for set-uid/set-gid programs */ if (Details.st_mode & S_ISUID) { if (Details.st_uid != getuid()) IssueWarning(FullPath,Details.st_uid,UID); } else if (Details.st_mode & S_ISGID) { if (Details.st_gid != getgid()) IssueWarning(FullPath,Details.st_gid,GID); } ControlExecutable( Buffer, EntryPoint, RestrictedFlag, TraceDefnName, argc-optind, &(argv[optind]), env); } else { ExitWithError(NO_PERROR, "Could not query the load of file %s\n",FullPath); } } else { /* * Error in "load" call. Use "execerror" to report the reasons * for failure. */ ErrorPtr=(char **)Buffer; ErrorPtr[0] = "execerror"; ErrorPtr[1] = FullPath; loadquery(L_GETMESSAGES, &ErrorPtr[2],sizeof(Buffer)-8); execvp("/usr/sbin/execerror",ErrorPtr); exit(1); } } libtrace.c ############################################################################## # libtrace_asm.s # -------------- # # "libtrace" assembly-language support routines. # This module contains the routines "GetDescriptorAddr" and "SetRegisters" # which are called from the main "libtrace" program. # # Author: Phil Gibbs - Trinem Consulting (pgibbs@trinem.co.uk) # Date: 14/12/1999 # ############################################################################## .globl .GetDescriptorAddr[pr] .csect .GetDescriptorAddr[pr] .function .GetDescriptorAddr[pr],GetDesc,16,044 # # Function "GetDescriptorAddr" # ---------------------------- # # The purpose of "GetDescriptorAddr" is to calculate which of the two compiler # "glue" routines (.glink or .ptgrl) has been used to call "GenericFunction" # and to return the address of the appropriate function descriptor which was # used to reference it. # # The two "glue" routines used by the compiler are as follows: # # .glink # ------ # lwz r12,offset(rTOC) # r12 <- function descriptor # stw rTOC,20(r1) # save TOC # lwz r0,0(r12) # r0 <- function address # lwz rTOC,4(r12) # set up TOC for new function # mtctr r0 # r0 -> ctr register # bctr # Branch to ctr register (CALL routine) # # Note: Useful side effect of this code is that it leaves the function # descriptor address in r12. # # .ptgrl # ------ # [[ enters with r11 => function descriptor ]] # # lwz r0,0(r11) # r0 <- function address # stw rTOC,20(r1) # save TOC # mtctr # ctr <- function address # lwz rTOC,4(r11) # set up TOC for new function # lwz r11,8(r11) # set up environment pointer # bctr # Branch to ctr register (CALL routine) # # Note: This code does not leave the function descriptor address anywhere as # r11 is overwritten with the environment pointer. However, the # environment pointer is not used in 'C' language routines. Therefore, # when setting up a new function descriptor to point to # "GenericFunction", "libtrace" will put the address of the function # descriptor into this environment pointer. Thus, the ".ptgrl" glue code # leaves the function descriptor address in r11. # # In order to determine which of the two "glue" routines has been used to call # GenericFunction, this function descends the stack finding the frame of the # caller (GenericFunction). The LR is always stored at offset 8 from the start # of the frame. This points to the instruction following the branch to the # glue routine. Therefore, we subtract 4 from the retrieved LR and use this # to locate the "bl" instruction. We then decode the "bl" instruction to get # the start address of the "glue" code and check the first two bytes to see if # it is ".glink" or ".ptgrl" code. The return value is then retrieved from # r11 or r12 accordingly. GetDesc: lwz 3,0(1); # Get to previous stack frame lwz 3,8(3); # LR always saved here. subi 3,3,4; # Point to "bl" instruction to gluecode lha 4,0(3); # get top 16 bits of the "bl" opcode andi. 4,4,0x03ff; # Mask out instruction bits andi. 5,4,0x0200; # Get sign bit and set condition bit mfcr 6 # save condition register slwi 4,4,16; # move to top 16 bits mr 5,4; # store away lha 4,2(3); # get bottom 16 bits of the "bl" opcode andi. 4,4,0xfffe; # get rid of "link" bit (bit 31) or 5,5,4; # join the two halves together mtcr 6; # restore condition register beq forwardREF # forward reference oris 5,5,0xfc00; # set top bits to negate result on add forwardREF: add 5,5,3; # add to address of "bl" instruction lha 3,0(5); # Get the first instruction of gluecode andi. 3,3,0xffff; # Ensure positive result. li 5,0x81; slwi 5,5,8; ori 5,5,0x82; # r5 now 0x8182 which is lwz r12,?(r2) cmp 0,3,5; # Is this "lwz r12,offset(r2)"? beq glink; # yes - must be .glink glue code. li 5,0x80; slwi 5,5,8; ori 5,5,0x0b; # r5 now 0x800b which is lwz r0,?(r11) cmp 0,3,5; # is this "lwz r0,offset(r11)"? beq ptgrl; # yes - must be .ptgrl glue code. fail: li 3,-1; # else FAIL condition. blr ptgrl: mr 3,11 # .ptgrl code - desc ptr in r11 blr glink: mr 3,12 # .glink code - desc ptr in r12 blr # and return .globl .SetRegisters[pr] .csect .SetRegisters[pr] .function .SetRegisters[pr],SetRegs,16,044 # # SetRegisters routine exists for AIX 4.2.1 and onwards. When entering __start # via the traced program's Entry Point a check is made if r30 and r31 are # equal. If not, then the module's __modinit routine is called. However, this # routine tries to do an indirect load from r30 which can result in segment # violations if r30 is not set to a sane value (which it isn't more often than # not when entered from "libtrace"!) Not sure what all this is about but # calling "SetRegisters" before entering the traced program sets r30 and r31 # to the same value and therefore avoids the (problematic) call to __modinit. # SetRegs: mr 30,31 # Register 30 = Register 31 li 3,0 # Reply = 0 (for completeness!) blr # return makefile #!/usr/bin/make -f # # Makefile for libtrace # #DEBUG_FLAG = -g LIBS = ld COMPILER = cc libtrace: libtrace.o libtrace_asm.o $(COMPILER) -l$(LIBS) libtrace.o libtrace_asm.o -o libtrace libtrace.o: libtrace.c $(COMPILER) libtrace.c $(DEBUG_FLAG) -c -o libtrace.o libtrace_asm.o: libtrace_asm.s $(COMPILER) libtrace_asm.s -c -o libtrace_asm.o traceip ############################################################################ # Trace definition file for libtrace # ---------------------------------- # # Lines are of the form: # # libname|call format specifier|return format specifier<|fail value> # # where: # libname is the name of the library function to trap. # call format specifier defines what to print on call. Note that the # library name is not explicitly printed and # needs to be included in the call format spec. # This is to allow for #defines to be invisibly # substituted (e.g.: accept/naccept). # return format specifier defines how to interpret the return value. # Note that the first parameter is the return # from the function, subsequent parameters are # the parameters passed to the function. Thus # in/out parameters (such as the string pointer # in "sprintf") can be optionally decoded on # return. # fail value Optional value. If specified causes "libtrace" # to print the value of errno if the return from # the function matches the fail value. # # comments commence with a '#' character and are ignored. # ############################################################################ # # Memory allocation # malloc|malloc(%ld)|0x%lx realloc|realloc(0x%lx,%ld)|0x%lx free|free(0x%lx)|%d # # Signal Handling # signal|signal(%d,0x%lx)|0x%lx sigaction|sigaction(%d,0x%lx,0x%lx)|%d sigvec|sigvec(%d,0x%lx,0x%lx)|%d # # Network connectivity # gethostbyname|gethostbyname("%s")|0x%lx|0 gethostname|gethostname(0x%lx,%d)|%d gethostid|gethostid()|%d socket|socket(%d,%d,%d)|%d|-1 listen|listen(%d,%d)|%d naccept|accept(%d,%d,%d)|%d setsockopt|setsockopt(%d,%d,%d,"%s",%d)|%d sendto|sendto(%d,0x%lx,%d,%d,0x%lx,%d)|%d nrecvfrom|recvfrom(%d,0x%lx,%d,%d,0x%lx,%d)|%d|-1 recv|recv(%d,0x%lx,%d,%d)|%d recvmsg|recvmsg(%d,0x%lx,0x%lx)|%d send|send(%d,0x%lx,%d,%d)|%d nsendmsg|sendmsg(%d,0x%lx,0x%lx)|%d select|select(%d,0x%lx,0x%lx,0x%lx,0x%lx)|%d inet_addr|inet_addr("%s")|0x%lx inet_network|inet_network("%s")|%ux inet_makeaddr|inet_makeaddr(%d,%d)|%lx getnetbyname|getnetbyname("%s")|0x%lx srcaddinet|srcaddinet(0x%lx,0x%lx,0x%lx)|0x%lx isinet_addr|isinet_addr("%s")|0x%lx # # File handling # chdir|chdir("%s")|%d mkdir|mkdir("%s",0x%lx)|%d getcwd|getcwd(0x%lx,%ld)|"%s" opendir|opendir("%s")|0x%lx readdir|readdir(0x%lx)|0x%lx unlink|unlink("%s")|%d read|read(%d,0x%lx,%d)|%d|-1 write|write(%d,"%s",%d)|%d|-1 fread|fread(0x%lx,%d,%d,0x%lx)|%d fwrite|fwrite(0x%lx,%d,%d,0x%lx)|%d fopen|fopen("%s","%s")|0x%lx open|open("%s",%d)|%d fclose|fclose(0x%lx)|%d close|close(%d)|%d ftell|ftell(0x%lx)|0x%lx fseek|fseek(0x%lx,0x%lx,%d)|0x%lx lseek|lseek(%d,0x%lx,%d)|0x%lx llseek|llseek(%d,0x%lx,0x%lx)|0x%lx fstat|fstat(%d,0x%lx)|%d stat|stat("%s",0x%lx)|%d lstat|lstat("%s",0x%lx)|%d fcntl|fcntl(%d,%d,0x%lx)|%d utimes|utimes("%s",0x%lx)|%d utime|utime("%s",0x%lx)|%d readlink|readlink("%s",0x%lx,%d)|%d # # String handling # fprintf|fprintf(0x%lx, "%s" ...)|%d sprintf|sprintf(0x%lx, "%s" ...)|%d ["%s"] scanf|scanf("%s" ...)|%d sscanf|sscanf(0x%lx,"%s" ...)|%d fscanf|fscanf(0x%lx,"%s" ...)|%d atol|atol("%s")|%ld atoi|atoi("%s")|%d gets|gets(0x%lx)|"%s" fgets|fgets(0x%lx,%d,0x%lx)|"%s" strtol|strtol("%s",0x%lx,%d)|%ld strcat|strcat("%s","%s")|%d strlen|strlen("%s")|%d strcmp|strcmp("%s","%s")|%d strncmp|strncmp("%s","%s",%d)|%d strcpy|strcpy(0x%lx,"%s")|"%s" strncpy|strncpy(0x%lx,"%s",%d|"%s" strcat|strcat("%s","%s")|"%s" strncat|strncat("%s","%s",%d)|%d strtok|strtok(0x%lx,"%s")|"%s" strchr|strchr("%s",'%c')|"%s" printf|printf("%s" ...)|%d puts|puts("%s")|%d # # "Exec" commands. Note successful calls to these functions replace the # traced image so tracing will stop. Therefore there is no reply from a # successful call to these functions. # execve|execve("%s",0x%lx,0x%lx)|%d|-1 execl|execl("%s",0x%lx,0x%lx..)|%d|-1 execle|execle("%s",0x%lx,0x%lx..)|%d|-1 execlp|execlp("%s",0x%lx,0x%lx..)|%d|-1 execv|execv("%s",0x%lx)|%d|-1 execvp|execvp("%s",0x%lx)|%d|-1 exect|exect("%s",0x%lx,0x%lx)|%d|-1 # # Basic process handling stuff. # pipe|pipe(0x%lx)|%d fork|fork()|%d exit|exit(%d)|%d # No reply from this function _exit|_exit(%d)|%d # or this one. getuid|getuid()|%d getgid|getgid()|%d getegid|geteuid()|%d getpid|getpid()|%d getppid|getppid()|%d system|system("%s")|%d wait|wait(0x%lx)|%d # # ODM access calls. # odm_initialize|odm_initialize()|%ld odm_open_class|odm_open_class(0x%lx)|0x%lx odm_lock|odm_lock("%s",%d)|%d odm_unlock|odm_unlock(%d)|%d odm_get_obj|odm_get_obj(0x%lx,"%s",0x%lx,%d)|0x%lx odm_set_path|odm_set_path("%s")|"%s" odm_mount_class|odm_mount_class("%s")|0x%lx odm_close_class|odm_close_class(0x%lx)|%d odm_get_list|odm_get_list(0x%lx,"%s",0x%lx,%d,%d)|0x%lx odm_free_list|odm_free_list(0x%lx,0x%lx)|%d odm_get_first|odm_get_first(0x%lx,"%s",0x%lx)|0x%lx odm_err_msg|odm_err_msg(%ld,0x%lx)|%d odm_get_by_id|odm_get_by_id(0x%lx,%d,0x%lx)|0x%lx odm_get_next|odm_get_next(0x%lx,0x%lx)|0x%lx odm_terminate|odm_terminate()|%ld # # Timer and time handing # getinterval|getinterval(0x%lx,0x%lx)|%d getitimer|getitimer(%d,0x%lx)|%d gettimeofday|gettimeofday(0x%lx,0x%lx)|%d localtime|localtime(0x%lx)|0x%lx asctime|asctime(0x%lx)|"%s" ctime|ctime(0x%lx)|"%s" time|time(0x%lx)|%ld alarm|alarm(%d)|%d sleep|sleep(%d)|%d # # Shared Memory calls # shmat|shmat(%d,0x%lx,0x%lx)|0x%lx shmctl|shmctl(%d,%d,0x%lx)|%d shmget|shmget(%d,0x%lx,%d)|%d shmdt|shmdt(0x%lx)|%d ftok|ftok("%s",'%c')|0x%lx # # RPC calls # getrpcent|getrpcent()|0x%lx getrpcbyname|getrpcbyname("%s")|0x%lx getrpcbynumber|getrpcbynumber(%d)|0x%lx setrpcent|setrpcent(%d)|%d endrpcent|endrpcent()|%d # # Miscellaneous General UNIX calls # ttyname|ttyname(%d)|"%s" isatty|isatty(%d)|%d getopt|getopt(%d,0x%lx,"%s")|%d getenv|getenv("%s")|"%s" ioctl|ioctl(%d,0x%lx,0x%lx)|%d ioctlx|ioctlx(%d,%d,0x%lx,%d)|%d perror|perror("%s")|%d getpwuid|getpwuid(%d)|0x%lx getpwnam|getpwnam("%s")|0x%lx putpwent|putpwent(0x%lx,0x%lx)|%d getpwent|getpwent()|0x%lx setpwent|setpwent()|%d endpwent|endpwent()|%d getgrent|getgrent()|0x%lx getgrnam|getgrnam("%s")|0x%lx getgrgid|getgrgid(%d)|0x%lx setgrent|setgrent()|%d endgrent|endgrent()|%d regcomp|regcomp(0x%lx,"%s",0x%lx)|%d regexec|regexec(0x%lx,"%s",%ld,0x%lx,0x%lx)|%d regerror|regerror(%d,0x%lx,0x%lx,0x%lx)|%ld regfree|regfree(0x%lx)|%d # # Miscellaneous Generic AIX calls # setlocale|setlocale(%d,"%s")|0x%lx pthread_create|pthread_create(0x%lx,0x%lx,0x%lx,0x%lx)|%d catopen|catopen("%s",%d)|0x%lx catgets|catgets(0x%lx,%d,%d,"%s")|0x%lx ptrace|ptrace(%d,%d,0x%lx,%d,0x%lx)|%d [0x%1$x] # see how dbx works!