/***************************************************************************/
/*                                                                         */
/*  injectso.c - Main file for injectso, inject a shared library into a    */
/*               running process.                                          */
/*                                                                         */
/*  Copyright (C) 2000 Shaun Clowes                                        */
/*                                                                         */
/*  This program is free software; you can redistribute it and/or modify   */
/*  it under the terms of version 2 of the GNU General Public License as   */
/*  published by the Free Software Foundation;                             */
/*                                                                         */
/*  This program is distributed in the hope that it will be useful,        */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of         */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
/*  GNU General Public License for more details.                           */
/*                                                                         */
/*  You should have received a copy of the GNU General Public License      */
/*  along with this program; if not, write to the Free Software            */
/*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
/*                                                                         */
/***************************************************************************/

#include <config.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <link.h>
#include <signal.h>
#include <stdlib.h>
#if OS_LINUX
   #include <sys/ptrace.h>
   #include <sys/user.h>
#elif OS_SOLARIS
   #include <procfs.h>
#endif
#include <injectso.h>
#include <readargs.h>
#include <errorlog.h>
#include <elffile.h>
#include <procdbg.h>

/* Globals                                                                 */
char *sProgName;

/* Private Prototypes                                                      */
void dump_dynamic(Elf32_Dyn *ptElfDyn);
void dump_linker_info(struct r_debug *ptDebug);
int call_dlopen(void *pvDlOpen, char *sLibName);
int call_userfunc(void *pvUserFunc, void *pvDynSection);
Elf32_Dyn *find_dynamic_entry(void *pvAddr, Elf32_Sword d_tag);
Elf32_Dyn *find_dynamic_entry_file(int iFd, off_t tOffset, 
                                   Elf32_Sword d_tag);
Elf32_Sym *find_sym(char *sSymbol, Elf32_Sym *ptSyms, int iNoSym, 
                    int iEntLen, char *pcStrings);
Elf32_Sym *find_sym_in_libs(char *sSym, struct r_debug *ptDebug);
Elf32_Sym *find_sym_in_map(char *sSym, struct link_map *ptLinkMap);
Elf32_Sym *find_sym_hash(char *sSymbol, Elf32_Sym *ptSyms, int iEntLen,
                         char *pcStrings, Elf32_Word *ptHashTable,
                         PSHashHeader ptHashHeader);
void dump_sym(Elf32_Sym *ptElfSym, char *pcStrings);

int main(int argc, char *argv[]) {
   Elf32_Dyn  *ptElfDyn;
   Elf32_Sym  *ptDlOpenSym = NULL, *ptUserSym;
   void *pvDlOpen;
   struct r_debug *ptDebug = NULL, tDebug;
   SProcStatus tSavedStatus;
   SFileInfo tFileInfo;

   /* Store our program name for later use                                 */
   sProgName = argv[0];

   /* Read in the arguments                                                */
   if (read_args(argc, argv)) 
      exit(1);

   /* Read the required information from the ELF file that is the target   */
   /* for injection                                                        */
   read_target_info(tProgArgs.sOptTarget, &tFileInfo);
   
   /* Check the specified shared library exists                            */
   check_library_info(tProgArgs.sOptLibrary);

   /* Open the target process                                              */
   if (ptrace_attach(tProgArgs.tOptPid)) 
      show_abort("Could not attach to pid %d, %s\n", tProgArgs.tOptPid, 
                 strerror(errno));

   show_debug(1, "Attached to process %d\n", tProgArgs.tOptPid);

   /* Wait for the remote process to stop                                  */
   wait_stop(STOP_SIGNAL);

   show_debug(1, "Process has stopped\n");

   /* Check the the DT_PLTGOT value in the dynamic segement of the         */
   /* matches the DT_PLTGOT value in the in core dynamic segment, if       */
   /* it doesn't we're either looking at the wrong file or the             */
   /* the linker has moved the segment for some weird reason               */
   if (!(ptElfDyn = find_dynamic_entry((void *) tFileInfo.tDynamicSegment.p_vaddr,
                                       DT_PLTGOT)))
      show_abort("Could not find PLTGOT dynamic segment entry in "
                 "target process, it is probably not the executable "
                 "file specified\n");

   if (memcmp(&tFileInfo.tPltGotDynamic, ptElfDyn, sizeof(tFileInfo.tPltGotDynamic)))
      show_abort("The DL_PLTGOT entry in the dynamic section of the executable "
                 "from the target file %s does not match the in memory version "
                 "in pid %d, it is probably not the executable file "
                 "specified\n", tProgArgs.sOptTarget, tProgArgs.tOptPid);

   /* Now read the DT_DEBUG entry, it contains the address of the          */
   /* dynamic linker debug struct we use to determine which shared         */
   /* libs are loaded where                                                */
   if (!(ptElfDyn = find_dynamic_entry((void *) tFileInfo.tDynamicSegment.p_vaddr,
                                       DT_DEBUG)) ||
       !(ptDebug = (struct r_debug *) ptElfDyn->d_un.d_val))
      show_abort("Debugging information has not been provided by the dynamic "
                 "linker\n");
   
   show_debug(1, "Found link map in process\n");

   /* Work out the interpreter this program uses                           */
   show_debug(2, "Dynamic linker for program is %s\n", tFileInfo.sInterp);

   /* We only know how to inject into programs dynamically linked          */
   /* with ld.so.1 on Solaris and ld-linux.so on Linux                     */
#if (OS_LINUX)
   if (!strstr(tFileInfo.sInterp, "ld-linux"))
      show_abort("Sorry, we can only inject into programs dynamically linked "
                 "by the linux dynamic linker ld-linux.so\n");
#elif (OS_SOLARIS)
   if (!strstr(tFileInfo.sInterp, "ld.so.1"))
      show_abort("Sorry, we can only inject into programs dynamically linked "
                 "by the Solaris dynamic linker ld.so.1\n");
#endif

   show_debug(3, "Dynamic linking debug struct is at 0x%08x\n", ptDebug);

   /* Read the struct in                                                   */
   if (sizeof(tDebug) != read_proc(ptDebug, &tDebug, sizeof(tDebug)))
      show_abort("Could not read dynamic linker debug information\n");

   /* Dump the dynamic linker info                                         */
   dump_linker_info(&tDebug);

   /* Now loop through the map examining the symbol tables of each of      */
   /* the libraries loaded into the remote process. We look for any        */
   /* symbols that match _dl_open or dlopen                                */
#ifdef OS_LINUX
   ptDlOpenSym = find_sym_in_libs("_dl_open", &tDebug);
#elif OS_SOLARIS
   ptDlOpenSym = find_sym_in_libs("dlopen", &tDebug);
#endif

   /* If we didn't find dlopen we're a gonner                              */
   if (!ptDlOpenSym)
      show_abort("Could not find dynamic library open routine\n");

   /* Ok, so we found dlopen, check if it has a value                      */
   pvDlOpen = (void *) ptDlOpenSym->st_value;   
   show_debug(2, "dlopen function found at 0x%08x of size %d\n",
              pvDlOpen, ptDlOpenSym->st_size);

   show_debug(1, "Found dynamic library open routine\n");

   /* Save the registers of the remote process (obviously we need to       */
   /* restore them after we're done                                        */
   memset(&(tSavedStatus), 0x0, sizeof(tSavedStatus));
   read_status(&(tSavedStatus));
#ifdef OS_LINUX
#ifdef CPU_IA32
   show_debug(3, "ESP of process is 0x%08x\n", tSavedStatus.regs.esp);
   show_debug(3, "EFLAGS of process is 0x%08x\n", tSavedStatus.regs.eflags);
#elif CPU_SPARC
   show_debug(3, "PC of process is 0x%08x\n", tSavedStatus.regs.pc);
   show_debug(3, "PSR of process is 0x%08x\n", tSavedStatus.regs.psr);
   show_debug(3, "SP of process is 0x%08x\n", tSavedStatus.regs.regs[13]);
#endif
#elif OS_SOLARIS
   show_debug(3, "PC of process is 0x%08x\n", tSavedStatus.pr_lwp.pr_reg[R_PC]);
   show_debug(3, "PSR of process is 0x%08x\n", tSavedStatus.pr_lwp.pr_reg[R_PSR]);
   show_debug(3, "SP of process is 0x%08x\n", tSavedStatus.pr_lwp.pr_reg[R_SP]);
#endif

   /* When we interrupt a syscall we end up destroying one of the          */
   /* parameters to the syscall. This is a problem because few programs    */
   /* are written to handle interrupted syscalls as they should (EINTR     */
   /* processing is missing) so we generally want to try to restart the    */
   /* syscall (if we interrupted one). restore_syscall takes care of that  */
   restore_syscall(&(tSavedStatus), tProgArgs.iOptNoSysRestart);

   /* Call dlopen in the remote process with the specified library         */
   call_dlopen(pvDlOpen, tProgArgs.sOptLibrary);

   show_debug(1, "Called dynamic open routine with library %s\n", tProgArgs.sOptLibrary);

   /* Now find the address of the the function the user asked us to        */
   /* execute and call it                                                  */
   if ((tProgArgs.sOptCallFunc) && (tProgArgs.sOptCallFunc[0] != '\0')) {
      ptUserSym = find_sym_in_libs(tProgArgs.sOptCallFunc, &tDebug);
      
      /* If we didn't find the function the user specified to execute      */
      /* this is an error (but not serious enough to abort)                */
      if (!ptUserSym || !(ptUserSym->st_value))
         show_error("Call function %s not found in target\n", tProgArgs.sOptCallFunc);
      else
         call_userfunc((void *) ptUserSym->st_value, 
                       (void *) tFileInfo.tDynamicSegment.p_vaddr);

      show_debug(1, "Called user function %s\n", tProgArgs.sOptCallFunc);
   }

#if OS_LINUX
#if CPU_IA32
   show_debug(3, "Setting remote process eip to 0x%08x\n", tSavedStatus.regs.eip);
#else
   show_debug(3, "Setting remote process pc to 0x%08x\n", tSavedStatus.regs.pc);
   show_debug(3, "Setting remote process npc to 0x%08x\n", tSavedStatus.regs.npc);
#endif
#elif OS_SOLARIS
   show_debug(3, "Restoring PC to 0x%08x\n", tSavedStatus.pr_lwp.pr_reg[R_PC]);
   show_debug(3, "Restoring nPC to 0x%08x\n", tSavedStatus.pr_lwp.pr_reg[R_nPC]);
   show_debug(3, "Restoring PSR to 0x%08x\n", tSavedStatus.pr_lwp.pr_reg[R_PSR]);
   show_debug(3, "Restoring SP to 0x%08x\n", tSavedStatus.pr_lwp.pr_reg[R_SP]);
#endif

   if (-1 == write_regs(&(tSavedStatus)))
      show_abort("Could not restore normal registers into target\n");
   show_debug(3, "Wrote regs!\n");

   read_status(&(tSavedStatus));

   show_debug(1, "Restored target executable state, detaching\n");

   /* Detach from the process we were (de)bugging                          */
   ptrace_detach();

   return(0);
}

/* call_dlopen() - Call dlopen in the remote process to load               */
/* the specified library                                                   */
int call_dlopen(void *pvDlOpen, char *sLibName) {
   char *psCoreLibName;
   SProcStatus tProcStatus;

   /* Push the library name onto the remote stack                          */
   psCoreLibName = (char *) push_stack_proc(sLibName, strlen(sLibName) + 1);

   /* Get the current registers in the remote process                      */
   read_status(&(tProcStatus));

#ifdef OS_LINUX
   /* So now we construct the args to the _dl_open function in the         */
   /* remote process                                                       */
   /* void __attribute__ ((regparm (3), stdcall)) *_dl_open                */
   /*                (const char *file, int mode, const void *caller)      */

#ifdef CPU_IA32
   /* file = shared library to be mapped (EAX)                             */
   /* mode = mode arguments to dlopen, e.g RTLD_LAZY, RTLD_NOW,            */
   /*        RTLD_GLOBAL, RTLD_LOCAL etc. (EDX)                            */
   /* caller = pointer to calling routine, not used unless $               */
   /*          occurs, see elf/dl-open.c in glibc source (ECX)             */

   tProcStatus.regs.ecx = 0x0;       /* Caller argument, not needed        */
   tProcStatus.regs.edx = RTLD_LAZY; /* Symbol resolution options          */
   tProcStatus.regs.eax = (unsigned long) psCoreLibName; /* Library Name   */
#elif CPU_SPARC
   /* file = shared library to be mapped (o0)                              */
   /* mode = mode arguments to dlopen, e.g RTLD_LAZY, RTLD_NOW,            */
   /*        RTLD_GLOBAL, RTLD_LOCAL etc. (o1)                             */
   /* caller = pointer to calling routine, not used unless $               */
   /*          occurs, see elf/dl-open.c in glibc source (o2)              */
   tProcStatus.regs.regs[R_O0] = (unsigned long) psCoreLibName;
   tProcStatus.regs.regs[R_O1] = RTLD_LAZY;
   tProcStatus.regs.regs[R_O2] = 0x0;
#endif /* CPU_SPARC                                                        */
#elif OS_SOLARIS     

   tProcStatus.pr_lwp.pr_reg[R_O0] = (prgreg_t) psCoreLibName;
   tProcStatus.pr_lwp.pr_reg[R_O1] = (prgreg_t) RTLD_LAZY;
#endif

   /* Write all the registers (the parameters) in                          */
   if (-1 == write_regs(&(tProcStatus)))
      show_abort("Could not construct dlopen parameters in target\n");
   
   /* Now call dlopen in the remote process                                */
   call_proc(pvDlOpen);

   return(0);
}

/* call_userfunc() - Call the function the user specified    in the        */
/* library that has been loaded into the remote process. We provide to     */
/* the function the address of the programs Dynamic section which should   */
/* be more than enough for it to do whatever it likes                      */
int call_userfunc(void *pvUserFunc, void *pvDynSection) {

   /* Construct the arguments to the function                              */
#if CPU_IA32
   /* Push the address of the Dynamic section of the target onto the       */
   /* stack                                                                */
   show_debug(3, "Dynamic segment address 0x%08x being written to process stack\n",
              pvDynSection);
   push_stack_proc((void *) &(pvDynSection), sizeof(pvDynSection));
#elif CPU_SPARC
   SProcStatus tProcStatus;

   show_debug(3, "Dynamic segment address 0x%08x being written to %%o0\n",
              pvDynSection);
   read_status(&(tProcStatus));   
#if OS_LINUX
   tProcStatus.regs.regs[R_O0] = (unsigned long) pvDynSection;
#elif OS_SOLARIS
   tProcStatus.pr_lwp.pr_reg[R_O0] = (prgreg_t) pvDynSection;
#endif
   write_regs(&(tProcStatus));
#endif

   /* Now call the intercept_begin function                                */
   call_proc((void *) pvUserFunc);

   return(0);
}

/* find_dynamic_entry() - Find an entry in the dynamic table at            */
/* the address specified in the program being debugged                     */
/* NOTE: The pointer returned is to a static variable in this              */
/* function, the program must not store just the resulting                 */
/* pointer but copy the whole thing if they want to keep it                */
Elf32_Dyn *find_dynamic_entry(void *pvAddr, Elf32_Sword d_tag) {
   int i = 0, iFound = 0;
   static Elf32_Dyn tElfDyn;

   show_debug(3, "Searching for dynamic record tag %d = %s in core\n",
              d_tag, dynamic_type(d_tag));
   while ((!iFound) &&
          (i < 30) && /* No file has more than 30 entries, SANITY          */
          (sizeof(tElfDyn) == read_proc(pvAddr, &tElfDyn, sizeof(tElfDyn))) &&
          (tElfDyn.d_tag != DT_NULL)) {
      show_debug(3, "0x%08x %s = 0x%08x\n", tElfDyn.d_tag, 
                 dynamic_type(tElfDyn.d_tag), tElfDyn.d_un.d_val);
      if (tElfDyn.d_tag == d_tag) 
         /* Excellent, this is the entry we're looking for                 */
         iFound = 1;
      pvAddr += sizeof(tElfDyn);
      i++;
   } 
   show_debug(3, "\n");

   if (iFound)
      return(&tElfDyn);
   else 
      return(NULL);
}

/* find_sym_in_libs() - Find the specified symbol in the remote            */
/* process by looking through the mapped shared libraries in               */
/* the process.                                                            */
/* NOTE: The pointer returned is to a static variable in this              */
/* function, the program must not store just the resulting                 */
/* pointer but copy the whole thing if they want to keep it                */
Elf32_Sym *find_sym_in_libs(char *sSym, struct r_debug *ptDebug) {
   Elf32_Sym *ptSym;
   struct link_map *ptLinkMap;

   ptLinkMap = ptDebug->r_map;
   ptSym = NULL;

#if OS_SOLARIS
   /* First search the dynamic linkers symbol tables                       */
   if ((ptSym = find_sym_in_map(sSym, ptDebug->r_ldsomap)))
      return(ptSym);
#endif

   /* Now we just search the link_map linked list provided by              */
   /* the dynamic linkers debug struct                                     */
   return(find_sym_in_map(sSym, ptLinkMap));
}

/* find_sym_in_map() - Find the specified symbol in a linked               */
/* list of link map structures                                             */
/* NOTE: The pointer returned is to a static variable in this              */
/* function, the program must not store just the resulting                 */
/* pointer but copy the whole thing if they want to keep it                */
Elf32_Sym *find_sym_in_map(char *sSym, struct link_map *ptLinkMap) {
   Elf32_Sym *ptSym, *ptElfSym;
   struct link_map tLinkMap;
   char sLibName[BUFSIZ];
   Elf32_Dyn *ptElfDyn;
   int iSymEntLen = sizeof(Elf32_Sym);
   char *pcStrings;
   Elf32_Word *ptHashTable = 0x0;
   SHashHeader tHashHeader;
#if OS_SOLARIS
   int iAbsoluteAddrs = 0;
#endif

   ptSym = NULL;
   while (ptLinkMap && !ptSym) {
      if (sizeof(tLinkMap) != read_proc(ptLinkMap, &tLinkMap, sizeof(tLinkMap)))
         show_abort("Could not read link map entry from target memory\n");

      /* We try to read in the library name here, to do that we read       */
      /* up to 255 chars, if the read aborts before then we don't          */
      /* care                                                              */
      ptLinkMap = tLinkMap.l_next;
      if (tLinkMap.l_name == NULL) 
         show_abort("Invalid library name referenced in dynamic linker map\n");
      read_proc(tLinkMap.l_name, &sLibName, sizeof(sLibName));
      if (*sLibName == '\0') 
         continue;
      show_debug(2, "Object %s, Loaded at 0x%08x\n", sLibName, tLinkMap.l_addr);

#if OS_SOLARIS
      /* Attempt to find a DT_DEBUG entry for the object, if it has one    */
      /* the object isn't relocatable and the offsets in the dynamic       */
      /* struct are absolute                                               */
      /* TODO: Couldn't it just be that the load base is 0 for the         */
      /* executable, check it out                                          */
        iAbsoluteAddrs = 0;
      if (find_dynamic_entry((void *) tLinkMap.l_ld, DT_DEBUG)) {
         show_debug(3, "Found DT_DEBUG in object, assuming absolute addresses\n");
         iAbsoluteAddrs = 1;
      } 
#endif

      /* Check the object has a dynamic section                            */
      if (!tLinkMap.l_ld) 
         show_abort("Could not find DYNAMIC section for object %s\n", sLibName);
      show_debug(3, "Dynamic section for library %s is at 0x%08x\n", sLibName, 
                 tLinkMap.l_ld);

      /* Find the dynamic symbol table for this library by looking in      */
      /* the DYNAMIC segment                                               */
      if (!(ptElfDyn = find_dynamic_entry((void *) tLinkMap.l_ld, DT_SYMTAB)))
         show_abort("Could not find dynamic symbol table for library %s\n", sLibName);

      ptElfSym = (Elf32_Sym *) ptElfDyn->d_un.d_ptr;
#if OS_SOLARIS
      if (!iAbsoluteAddrs)
         ptElfSym = (Elf32_Sym *) ((unsigned long) ptElfSym + 
                                   (unsigned long) tLinkMap.l_addr);
#endif
      
      /* We need to know the length of each symbol table entry             */
      if ((ptElfDyn = find_dynamic_entry((void *) tLinkMap.l_ld, DT_SYMENT)))
         iSymEntLen = ptElfDyn->d_un.d_val;

      /* We need a strings table too to be able to find anything           */
      if (!(ptElfDyn = find_dynamic_entry((void *) tLinkMap.l_ld, DT_STRTAB)))
         show_abort("No string table found for library %s\n", sLibName);
      pcStrings = (char *) ptElfDyn->d_un.d_ptr;
#if OS_SOLARIS
      if (!iAbsoluteAddrs)
         pcStrings = (char *) (pcStrings + (unsigned long) tLinkMap.l_addr);
#endif

      /* There must be a hash table for this to be a valid dynamic ELF     */
      /* binary. We need one to determine the number of symbols in the     */
      /* file and can also use it to speed up our search                   */
      if ((ptElfDyn = find_dynamic_entry((void *) tLinkMap.l_ld, DT_HASH))) {
#if OS_LINUX
         ptHashTable = (Elf32_Word *) (ptElfDyn->d_un.d_ptr + 
                                       (unsigned long) tLinkMap.l_addr);
#elif OS_SOLARIS
         ptHashTable = (Elf32_Word *) ptElfDyn->d_un.d_ptr;
         if (!iAbsoluteAddrs)
            ptHashTable = (Elf32_Word *) ((char *) ptHashTable + 
                                          tLinkMap.l_addr);
#endif
         show_debug(3, "Found hash table for library %s at 0x%08x\n", 
                    sLibName, ptHashTable);

         /* Read the hash table header                                     */
         if (sizeof(tHashHeader) != read_proc(ptHashTable, &tHashHeader, 
                                              sizeof(tHashHeader)))
            show_abort("Could not read hash table header\n");
      } else
         show_abort("Could not find hash table in target process\n");

      if (!(tProgArgs.iOptNoHash))
         ptSym = find_sym_hash(sSym, ptElfSym, iSymEntLen, pcStrings, 
                               ptHashTable, &tHashHeader);
      else
         ptSym = find_sym(sSym, ptElfSym, tHashHeader.tChains, iSymEntLen, 
                          pcStrings);
   }

   /* When we find the symbol modify its value to include the              */
   /* offset in the library                                                */
   if (ptSym)
      ptSym->st_value = (tLinkMap.l_addr + ptSym->st_value);

   return(ptSym);
}

/* find_sym_hash() - Find a specified symbol in a symbol table with the    */
/* aid of a hash table                                                     */
/* NOTE: The pointer returned is to a static variable in this              */
/* function, the program must not store just the resulting                 */
/* pointer but copy the whole thing if they want to keep it                */
Elf32_Sym *find_sym_hash(char *sSymbol, Elf32_Sym *ptSyms, int iEntLen,
                         char *pcStrings, Elf32_Word *ptHashTable,
                         PSHashHeader ptHashHeader) {
   Elf32_Word *ptBuckets, *ptChains;
   Elf32_Word tBucket, tChain, tNextChain;
   int iFound = 0, iReadBytes = 0;
   static Elf32_Sym tElfSym;
   Elf32_Sym *ptSym;
   unsigned long ulHash, ulHashBucket;
   char sSymName[256];

   ptBuckets = ptHashTable + 2;
   ptChains = ptHashTable + 2 + ptHashHeader->tBuckets;

   show_debug(3, "Hash table has %u buckets (at 0x%08x) and %u chains (at 0x%08x)\n", 
              ptHashHeader->tBuckets, ptBuckets, ptHashHeader->tChains, ptChains);

   ulHash = calc_elf_hash(sSymbol);
   ulHashBucket = ulHash % ptHashHeader->tBuckets;
   show_debug(3, "Hash of %s is 0x%08x, or bucket %u\n", sSymbol, ulHash, ulHashBucket);

   /* Read in the bucket entry for the given hash                          */
   if (sizeof(tBucket) != read_proc(ptBuckets + ulHashBucket, &tBucket, sizeof(tBucket)))
      show_abort("Could not read hash bucket\n");

   show_debug(3, "Hash bucket %u contains %u\n", ulHashBucket, tBucket);

   show_debug(2, "Searching chain for symbol %s\n", sSymbol);
   tNextChain = tBucket;
   while ((!iFound) && 
          ((tChain = tNextChain) != STN_UNDEF) && 
          (sizeof(tNextChain) == read_proc(ptChains + tChain, &tNextChain, sizeof(tNextChain)))) {
      /* Read in the symbol table entry                                    */
      ptSym = (Elf32_Sym *) ((char *) ptSyms + (iEntLen * tChain));

      if (sizeof(tElfSym) != read_proc(ptSym, &tElfSym, sizeof(tElfSym)))
         show_abort("Could not read symbol at 0x%08x\n", ptSym);

      /* We're only interested in entries that have a name, are not zero   */
      /* size and that refer to functions (STT_FUNC, this flag is only     */
      /* used in shared libraries)                                         */
      if (!tElfSym.st_name)
         continue;
      if (!tElfSym.st_size)
         continue;
      if (!(ELF32_ST_TYPE(tElfSym.st_info) == STT_FUNC))
         continue;
      if (!tElfSym.st_value)
         continue;

      /* Read in the symbol name, anything more than 0 bytes is ok         */
      if (!(iReadBytes = read_proc(pcStrings + tElfSym.st_name,
                                   sSymName, sizeof(sSymName) - 1))) {
         show_debug(1, "Could not read dynamic symbol\n");
         break;
      }

      /* Make sure the name is null terminated                             */
      sSymName[iReadBytes] = '\0';
      show_debug(3, "Hash Symbol %s, Length %d, Value 0x%08x\n", 
                 sSymName, tElfSym.st_size, tElfSym.st_value);

      /* See if this is the symbol they're looking for                     */
      if (!strcmp(sSymbol, sSymName))
         iFound = 1;
   }
   show_debug(3, "\n");
          
   if (iFound)
      return(&tElfSym); 
   else 
      return(NULL);
}

/* find_sym() - Find an specified symbol in a symbol table (without a      */
/* hash table)                                                             */
/* NOTE: The pointer returned is to a static variable in this              */
/* function, the program must not store just the resulting                 */
/* pointer but copy the whole thing if they want to keep it                */
Elf32_Sym *find_sym(char *sSymbol, Elf32_Sym *ptSyms, int iNoSym, 
                    int iEntLen, char *pcStrings) {
   int i = 0, iFound = 0, iReadBytes = 0;
   static Elf32_Sym tElfSym;
   Elf32_Sym *ptSym;
   char sSymName[256];

   show_debug(2, "Searching for symbol %s\n", sSymbol);
   ptSym = ptSyms;
   while ((!iFound) &&
          (i < 5000) && /* No symbol table has over 5000 entries, SANITY   */
          (i < iNoSym) &&
          (sizeof(tElfSym) == (iReadBytes = read_proc(ptSym, &tElfSym, sizeof(tElfSym))))) {
      /* Incrememnt the loop vars now incase we just 'continue'            */
      ptSym = (Elf32_Sym *) ((unsigned long) ptSym + (unsigned long) (iEntLen));
      i++;
      /* We're only interested in entries that have a name, are not zero   */
      /* size and that refer to functions (STT_FUNC, this flag is only     */
      /* used in shared libraries)                                         */
      if (!tElfSym.st_name)
         continue;
      if (!tElfSym.st_size)
         continue;
      if (!(ELF32_ST_TYPE(tElfSym.st_info) == STT_FUNC))
         continue;
      if (!tElfSym.st_value)
         continue;

      /* Read in the symbol name, anything more than 0 bytes is ok         */
      if (!(iReadBytes = read_proc(pcStrings + tElfSym.st_name,
                                   sSymName, sizeof(sSymName) - 1))) {
         show_debug(1, "Could not read dynamic symbol\n");
         break;
      }

      /* Make sure the name is null terminated                             */
      sSymName[iReadBytes] = '\0';
      show_debug(3, "Symbol %s, Length %d, Value 0x%08x\n", 
                 sSymName, tElfSym.st_size, tElfSym.st_value);

      /* See if this is the symbol they're looking for                     */
      if (!strcmp(sSymbol, sSymName))
         iFound = 1;
   } 
   show_debug(3, "\n");

   if (iFound)
      return(&tElfSym); 
   else 
      return(NULL);
}

/* dymp_syn - Pring a symbol table entry to debug output                   */
void dump_sym(Elf32_Sym *ptElfSym, char *pcStrings) {
   char sSymName[BUFSIZ];

   show_debug(3, "Name at 0x%08x, Size is 0x%08x, Addr is 0x%08x\n", 
              ptElfSym->st_name, ptElfSym->st_size, ptElfSym->st_value);
   if (ptElfSym->st_name) {
      read_proc(pcStrings + ptElfSym->st_name, sSymName, BUFSIZ - 1);
      show_debug(3, "Symbol is %s\n", sSymName);
   }
   
}

/* dump_linker_info - Print the dynamic linker debug header to             */
/* debug output                                                            */
void dump_linker_info(struct r_debug *ptDebug) {

   show_debug(3, "Dynamic Linker Debugging Information\n");
   show_debug(3, "------------------------------------\n");
   show_debug(3, "Version:                         %d\n", ptDebug->r_version);
   show_debug(3, "Rmap head:                       0x%08x\n", ptDebug->r_map);
   show_debug(3, "Dynamic linker base address:     0x%08x\n", ptDebug->r_ldbase);
   show_debug(3, "\n");

}

