/*-------------------------------------------------------------------------
** Name: adlookup.cpp
**
** Title: Address Lookup Routines
**
** Purpose:
**  Supplies the routines defined in symblsvr.h for looking up a symbol by
**  its address.  "address" is an abstract term which can be:
**      physical address
**      offset from stack frame context
**      register index for register variables
**
** Status: PRELIMINARY |
**
** $Log:   S:/tbird/mt2_186/symbol/adlookup.cpv  $
** 
**    Rev 1.3   04 Jun 1998 14:20:08   hera
** 
**    Rev 1.2   22 Jul 1997 10:10:06   Judy
** 
**    Rev 1.1   26 Feb 1997 11:42:20   Judy
** 
**    Rev 1.1   14 Jan 1997 15:30:26   Judy
** No change.
** 
**    Rev 1.0   14 Jun 1996 16:41:52   Judy
** Initial revision.
** 
**    Rev 1.23   04 Nov 1994 10:10:00   nghia
** Fixed bug for PV 2.4 - Added check for x86 processor before calling to get
** segment and selector information.
** 
**    Rev 1.22   26 Sep 1994 10:27:08   nghia
** Revised SymMapAddr2Symbol to extract gdt:ldt:offset of address correctly
** for Real/Protected mode symbols.
** 
**    Rev 1.21   22 Aug 1994 18:36:20   brucea
** Changed global variable NoPublics to FilterPublics
** 
**    Rev 1.20   03 Aug 1994 12:07:44   brucea
** Added: FilterPublics global var
** Modified: calls to SearchForAddr to pass the FilterPublics parameter
** 
**    Rev 1.19   28 Jul 1994 13:16:10   brucea
** Changed error message back to ER_ADDRESS_NOT_FOUND
** 
**    Rev 1.18   25 Jul 1994 12:55:48   brucea
** Support virtual addresses in SymMapAddr2Symbol
** 
**    Rev 1.17   14 Jul 1994 01:59:10   brucea
** Extensive modifications to SymMapAddr2Symbol to support
** linear/virtual, real/protected, USER/SMM modes, and to clean
** up the algorithm and document it.
** 
**    Rev 1.16   28 Feb 1994 15:46:54   marilyn
** Removed references to ADDR_LOGICAL.
** 
**    Rev 1.15   27 Apr 1993 16:56:02   nghia
** Fixed bug: Symbol server failed to search for symbol when load Sierra 
** file with mergeSections is ON.  This problem occurred when section bases are
** overlapped and the cacheAddrLookupBaseOffset is the last base on the list.
** According to Bruce, it should continue at the top of the list and search 
** again.  
** 
**    Rev 1.14   18 Dec 1992 18:20:14   brucea
** Cleanup: removed localRetCode, took out (U8) cast for .symType & 0xF
** 
**    Rev 1.13   12 Aug 1992 10:07:56   brucea
** Bug fix in SymMapLinenum2FuncOrBlock: was not returning correct outputSymbol;
**    now returns outSymbol rather than funcDescriptor
** 
** 
**    Rev 1.12   06 Aug 1992 10:17:32   brucea
** Changed access to .typeIndex.symType to use &0xF
** 
**    Rev 1.11   20 Jul 1992 16:18:10   brucea
** Changed: search for parent function now skips over blocks until function found
** Cosmetic changes
** 
**    Rev 1.10   19 Jul 1992 22:06:26   brucea
** Fixed bug: address lookup wasn't returning correct symbol type
** 
**    Rev 1.9   10 Jul 1992 19:03:04   brucea
** Added: check at beginning of SymMapAddr2Symbol for whether there are any bases
**    which indirectly indicates if any symbols are loaded or not.  This was put
**    in for C&T because they use virtual addresses
** 
**    Rev 1.8   15 Jun 1992 15:34:24   brucea
** Commented out: return localRetCode because of patch - should be resolved
** 
**    Rev 1.7   20 Apr 1992 08:44:28   brucea
** Added: AdlookupREsetCacheVars to fix PPR5473
** 
**    Rev 1.6   27 Feb 1992 22:48:34   brucea
** Added: Gross patch to SymMapAddr2Symbol to force an address search to continue
**        to other based even though it was in the range of another base and yet
**        the symbol was not found.  This temporarily patches the problem that
**        the ????STACKTOP public var is being added to the variable base and
**        setting the minimum address to cover all the code addresses.  This
**        needs to be fixed in the loader.
** 
**    Rev 1.5   11 Dec 1991 17:06:10   brucea
** Removed MAX_BASE_INDEX_SIZE
** 
**    Rev 1.4   11 Dec 1991 16:18:52   brucea
** Added tests for return value for all addr.h Adr... calls
** Added test for selector not a valid base index
** 
**    Rev 1.3   09 Dec 1991 17:28:44   brucea
** Changes follow:

** In SymMapAddr2Symbol, added SYM_BLOCK to allowable symbol type return in
**   the funcDescriptor slot.
** Implemented SymMapLinenum2FuncOrBlock
** 
**    Rev 1.2   29 Nov 1991 20:01:28   brucea
** changed LOGICAL_ADDR_TYPE to DESCRIPTOR
** NULL to NULL_SYMBOL
** Added calls to addr.h routines: AdrGetAddrType,
**   AdrGetLdtSelector, AdrGetAddrOffset
** Process the address different to support selector:offset or
**   logical addresses
** Changed logicalAddr to OffsetAddr
** 
**    Rev 1.1   18 Nov 1991 15:34:38   brucea
** Changed first parameter to SymMapAddr2Symbol to LOGICAL_ADDR_TYPE
** Changed code with accessed structure; this code will change with
**    new addr.h
** 
**    Rev 1.0   01 Nov 1991 11:04:12   brucea
** Initial revision.
** 
** $Header:   S:/tbird/mt2_186/symbol/adlookup.cpv   1.3   04 Jun 1998 14:20:08   hera  $
**
** Copyright (C) 1991 Microtek International.  All rights reserved.
**
**------------------------------------------------------------------------*/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/
#ifndef _ADDR_
#include "addr.h"
#endif

#ifndef _PROC_
#include "proc.h"
#endif

#ifndef _SYMBLSVR_
#include "symblsvr.h"
#endif

#ifndef _BASETBL_
#include "basetbl.h"
#endif

#ifndef _BYADRTBL_
#include "byadrtbl.h"
#endif

                       /****************************
                        *                          *
                        *        EXTERNALS         *
                        *                          *
                        ****************************/

extern BaseIndexTable bit; // base index table
extern MemPool st;         // symbol table
extern PROCESSOR_FAMILY globalProcFamily;

//--------------------------------------------------------------------------
// AdlookupResetCacheVars
//--------------------------------------------------------------------------
VOID AdlookupResetCacheVars(VOID);

                        /***************************
                        *                          *
                        *         GLOBALS          *
                        *                          *
                        ****************************/
BOOLEAN FilterPublics = FALSE; // used to gate by-addr search to ignore
                               // publics or not

                        /***************************
                        *                          *
                        *      LOCAL STATICS       *
                        *                          *
                        ****************************/

PRIVATE TABLE_OFFSET cachedAddrLookupBaseOffset;

                       /****************************
                        *                          *
                        *     EXECUTABLE CODE      *
                        *                          *
                        ****************************/


//--------------------------------------------------------------------------
// AdlookupResetCacheVars
//--------------------------------------------------------------------------
VOID AdlookupResetCacheVars(VOID) {

   cachedAddrLookupBaseOffset = NULL_SYMBOL;
}


//--------------------------------------------------------------------------
// SymMapAddr2Symbol
//
// Purpose:
//    Map an abstract address to a symbol that contains it.  It returns
//    the symbol with a starting address closest to the requested
//    address.  It also returns its type and parent information.  This
//    routine returns a symbol descriptor, then the caller
//    makes another call to get specific information about the symbol.
//
// Input parameters;
//    addr: abstract address to be looked up.
//
// Output parameters:
//    memoryClass:
//       what basic symbol class; to be completed with hints when they are
//       implemented.  If an address match is not found, the memoryClass
//       value gives the caller more general information about the address
//    symbolType: basic symbol type - part of every symbol
//    offset:
//        distance the requested address is from the function's start address
//    outputSymbol:
//        descriptor to the symbol found that is closest to the requested addr
//    funcDescriptor: offset to parent function; can be a function or unnamed
//        block; NULL if none exists
//    moduleDescriptor: offset to parent module; NULL if none exists
//
// Error:
//    Error reported if address type is not linear or virtual
//    Error reported if no symbol match is found.
//
// Rules:
//    if address is virtual, the segment or selector is stored in the
//       base object.  For real mode, segments have to match and
//       USER/SMM must match.
//       For protected mode, the selectors must match (GDT and LDT, where
//       LDT = 0 if none used).
//       Once a match is found, the offset is compared normally with no
//       subtraction.
//    Start address search with cached base index (if valid).  If address
//       doesn't match base range, then start at beginning of base linked
//       list and search for match.  Once found, save found base in cache.
//       Find base that contains the address requested.
//    When a base is found that contains the requested address, do a binary
//       search on the ByAddressTable to find a symbol that contains the
//       requested address.  It is possible not to find the address because
//       the end address may not include the address requested; i.e. some
//       symbols like labels may have a range of 1.  Return error
//       ER_ADDRESS_NOT_FOUND if this occurs.
//    Search down in the table for an address range whose start address is
//       closer to the requested address.  Stop when it exceeds requested addr
//    Initialize return descriptors to NULL.
//    Access symbol.  Fill in <symbolType>, <offset>, <memoryClass>, and
//       <outputSymbol>. If any parent is a function, fill in
//       <funcDescriptor>.  If any parent is a module (may be a public which
//       points to root) fill in <moduleDescriptor>.
//
// Pseudo-code:
//
//    get search params from address descriptor
//    set handledCachedPtr = FALSE
//    if valid cached baseptr
//       get cached baseptr
//    else
//       set handledCachedPtr = TRUE
//    do
//       compare with current base/offset
//       1. Virtual and real:
//          Compare segment and USER/SMM.
//          Then see if addr offset is within base range.
//          Then see if addr has a symbol match
//       2. Virtual and protected:
//          Compare input selector to stored GDT selector.
//          If input ldt != 0, compare it to LDT selector.
//          Ignore ADDR_SEGSEL_TYPE, since the segSel value is always valid
//          Then see if addr offset is within base range.
//          Then see if addr has a symbol match
//       3. Linear and real:
//          If input offset >= base, subtract base from offset else fail
//          Take result and see if it is within base range.
//          Then see if addr has a symbol match
//       4. Linear and protected:
//          Same as real
//       5. Linear and non-Intel:
//          Same as real
//       if match, break
//       if (handledCachedPtr == FALSE)
//          get pointer to beginning of base list
//          handledCachedPtr = TRUE
//    while (table pointer != NULL)
//    if (table pointer == NULL) return with "cannot find address in sym table"
//------------------------------------------------------------------------
RETCODE EXPORT
SymMapAddr2Symbol(DESCRIPTOR     addrDesc,
                  MEM_ADDR_CLASS *memoryClass,
                  SYM_TYPE_TYPE  *symbolType,
                  U32            *offset,
                  SYM_DESCRIPTOR *outputSymbol,
                  SYM_DESCRIPTOR *funcDescriptor,
                  SYM_DESCRIPTOR *moduleDescriptor)  {

   RETCODE           err;
   TABLE_OFFSET      tableOffset, byAddrOffset;
   BaseSymbol        HUGE* basePtr;       // ptr to Base object
   ByAddressTable    HUGE* byAddrPtr;     // ptr to by-address table object
   COMMON_SYMBOL_HEADER HUGE* symPtr;     // ptr to a symbol common header
   OFFSET_ADDR_TYPE  searchOffsetAddr; // value to compare against
   SYMBOL_INFO_TYPE  symbolInfo;
   BOOLEAN           handledCachedPtr = FALSE;
   BOOLEAN           found;
   U32               requestedAddr; // input addr
   ADDR_SEGSEL_TYPE  segSelType;  // query interpretation of selector
   ADDR_TYPE         addrType;
   U16               addrSegSel;  // query segment/selector
   U16               gdtSel = 0;  // query gdt selector - ldtsel of address
   ADDR_MODE         addrMode;    // query address mode
   ADDR_SPACE        addrSpace;   // query address space
   
   // check for symbols loaded or not
   if (NULL_SYMBOL == (tableOffset = bit.GetRootBaseSymbolOffset())) {
      return ER_ADDRESS_NOT_FOUND;  // "Cannot find address in symbol table"
   }

   // initialize the return parameters to the status of the address NOT FOUND
   *memoryClass = UNDEFINED_ADDR;
   *symbolType =  SYM_UNDEFINED;
   *offset = 0L;
   *outputSymbol = *funcDescriptor = *moduleDescriptor = NULL_SYMBOL;

   // Extract individual pieces out of requested address descriptor
   if (GOOD !=(err = AdrGetAddrOffset(addrDesc, &requestedAddr)))
      return err;

   if (GOOD != (err = AdrGetAddrType(addrDesc, &addrType)))
      return err;

   if (globalProcFamily == FAMILY_X86) {
      // if mode is MODE_CURRENT, this function uses pmode to
      // determine actual mode.
      if (GOOD != (err = AdrTranslateAddrMode(addrDesc, &addrMode)))
         return err;
      // Notes: 09/26/94 - Nghia
      // only get the ldt selector (Symbol's gdtSel) if in protected mode
      // get gdt selector value - ldt selector of Address descriptor
      if ((addrType == ADDR_VIRTUAL) &&
          ((addrMode == MODE_PROT16) || (addrMode == MODE_PROT32)) &&
          (GOOD != (err = AdrGetLdtSelector(addrDesc, &gdtSel))))
         return err;
      // get the segment/selector value.  It is always valid even if the
      // ADDR_SEGSEL_TYPE is ADDR_USE_XX, because the caller has filled it in.
      if (GOOD != (err = AdrGetAddrSegmentSelector(addrDesc,
                                                   &segSelType,
                                                   &addrSegSel)))
         return err;
   }

   // get base pointer to base symbol to compare against first
   if (cachedAddrLookupBaseOffset != NULL_SYMBOL)  {
      tableOffset = cachedAddrLookupBaseOffset;
   } else {
      // go thru while loop from beginning
      // get beginning of base table
      handledCachedPtr = TRUE;
      tableOffset = bit.GetRootBaseSymbolOffset(); // get the first base
   }
   found = FALSE;
   
   do {
      // tableOffset is always valid at this point and is the offset
      // into st pointing to the base; need to dereference into an
      // actual base ptr
      basePtr = (BaseSymbol HUGE*)st.GetHugeDataPtr(tableOffset);
      
      // comparison starts here
      switch (addrType) {
       //*****************
         case ADDR_LINEAR:    // algorithm same for Intel and Moto
         case ADDR_PHYSICAL: {
       //*****************
         U32 segment;
         // if input offset >= base, subtract base from offset else fail
	 if (requestedAddr >= (basePtr->GetBaseAddress())) {
            segment = basePtr->GetBaseAddress();
	    searchOffsetAddr = requestedAddr - segment;
            segment = (segment >> 4);
            if ((basePtr->CompareAddrVars(searchOffsetAddr)))  {

               // linear address inside base object; now see if symbol match
               // do a binary search on by-addr table for symbol match
               byAddrOffset = basePtr->GetByAddressTableOffset();
               byAddrPtr = (ByAddressTable HUGE*)st.GetHugeDataPtr(byAddrOffset);
	       if (GOOD == byAddrPtr->SearchForAddr(searchOffsetAddr,
                                                    (U16)segment,
                                                    FilterPublics,
                                                    symbolInfo)) {
                  found = TRUE;
               }
            }
         }
         break;
         } // end of ADDR_LINEAR case

       //******************
         case ADDR_VIRTUAL:  {
       //******************
         // branch according to protected/real mode; get addrMode
         if ((addrMode == MODE_PROT16) || (addrMode == MODE_PROT32)) {
            // selectors have to match
            if (gdtSel == (U32)basePtr->GetGDTsel()) {
               // skip ldt if 0; otherwise must match loaded value
               if ((addrSegSel == 0L) ||
                   ((addrSegSel != 0L) &&
                   (addrSegSel == basePtr->GetSegOrLDTsel()))) {
                  // now compare requested offset with stored base offset range
                  if ((basePtr->CompareAddrVars(requestedAddr)))  {
                     // there is match with offset range
                     // do a binary search on by-addr table for symbol match
                     byAddrOffset = basePtr->GetByAddressTableOffset();
                     byAddrPtr = (ByAddressTable HUGE*)
                                 st.GetHugeDataPtr(byAddrOffset);
                     if (GOOD == byAddrPtr->
				 SearchForAddr(requestedAddr,
                                               addrSegSel,
                                               FilterPublics,
                                               symbolInfo)) {
                        found = TRUE;
                     }
                  }
               }
            }
         } else {  // real mode
            // NOTE: code does not check for MODE_NONE (should never occur
            //       for virtual mode)
            // segments have to match
            if (addrSegSel == basePtr->GetSegOrLDTsel()) {
               // compare USER/SMM;
               // get address space
               if (GOOD != (err = AdrGetAddrSpace(addrDesc, &addrSpace)))
                  return err;
               if (((addrSpace == SPACE_USER) &&
                    (basePtr->GetSegAddrSpace() == SEG_SPACE_USER)) ||
                   ((addrSpace == SPACE_SMM) &&
                    (basePtr->GetSegAddrSpace() == SEG_SPACE_SMM))) {

                  // now compare requested offset with stored base offset range
                  if ((basePtr->CompareAddrVars(requestedAddr)))  {

                     // there is match with offset range
                     // do a binary search on by-addr table for symbol match
                     byAddrOffset = basePtr->GetByAddressTableOffset();
                     byAddrPtr = (ByAddressTable HUGE*)
                                 st.GetHugeDataPtr(byAddrOffset);
                     if (GOOD == byAddrPtr->
				 SearchForAddr(requestedAddr,
					       addrSegSel,
                                               FilterPublics,
                                               symbolInfo)) {
                        found = TRUE;
                     }
                  }
               }
            }
         }
         break;
         }  // end of ADDR_VIRTUAL case
         
         default: return ER_INVALID_ADDRESS_TYPE;
         // end of case ADDR_PHYSICAL
      }  // end of switch

      if (found)
         break;  // exit do-while loop
      if (handledCachedPtr == TRUE) {
         tableOffset = basePtr->GetNextBaseOffset();
      } else {
         // cached pointer did not find symbol match
         // get pointer to beginning of base list, and start search across
         // all bases
         tableOffset = bit.GetRootBaseSymbolOffset(); // get the first base
         handledCachedPtr = TRUE;
      }
   } while (tableOffset != NULL_SYMBOL);  // end of do-while loop

   if (tableOffset == NULL_SYMBOL) {
      return ER_ADDRESS_NOT_FOUND;  // "Cannot find address in symbol table"
   }
   cachedAddrLookupBaseOffset = tableOffset;  // save away found base object

   //**************************************************
   // symbolInfo structure holds symbol information found
   // now reusing tableOffset
   //**************************************************

   // save information retrieved
   *offset = symbolInfo.delta;
   *outputSymbol = tableOffset = symbolInfo.symbolOffset;
   symPtr = (COMMON_SYMBOL_HEADER HUGE*)st.GetHugeDataPtr(tableOffset);
   // must mask packed structure to get unsigned value for switch statement
   *symbolType = symPtr->typeIndex.symType & 0xF;
   // set memoryClass to code or data;  previously set to UNDEFINED_ADDR
   switch (*symbolType) {
      case SYM_GLOBAL_VAR:
      case SYM_LOCAL_VAR:
      case SYM_USER_DEFINED_VAR:
      case SYM_PUBLIC_VAR:
         *memoryClass = DATA_ADDR;
         break;

      default:
         *memoryClass = CODE_ADDR;
         break;
   }  // end of switch

   // fill in the funcDescriptor and moduleDescriptor fields; must follow the
   // parent symbol linkages

   SYM_TYPE_TYPE     tmpType;
   BOOLEAN           funcFound = FALSE;

   while (tableOffset != NULL_SYMBOL) {
      // get symbol pointer, then its type
      symPtr = (COMMON_SYMBOL_HEADER HUGE*)st.GetHugeDataPtr(tableOffset);
      tmpType = (symPtr->typeIndex.symType) & 0xF;
      switch(tmpType) {
         case SYM_ROOT:
              SYM_UNDEFINED:
            return GOOD;

         case SYM_MODULE:
            *moduleDescriptor = tableOffset;
            return GOOD;

	 case SYM_BLOCK:
	    //Hera 6/3/97
	    if (!funcFound)
	       *funcDescriptor = tableOffset;
            break;      // don't stop at block; find function parent

         case SYM_FUNCTION:  // match function or block
            if (!funcFound) {
               *funcDescriptor = tableOffset;  // found parent function
               funcFound = TRUE;  // once found, ignore any other higher
                                  // level nested functions
               break;
            }
      } // end of switch
      tableOffset = symPtr->symParentOffset;
   } // end of while loop
   return GOOD;
}  // end of SymMapAddr2Symbol


//--------------------------------------------------------------------------
// SymMapLinenum2FuncOrBlock
//
// Purpose:
//    Given a line number and its context, look up the most local function or
//    block that contains it.  Return the type (function or block), and a
//    descriptor to it.
//
// Error:
//    Reports error if:
//        moduleName not found
//        linenum not in module context
//        type returned is not a function or block
//--------------------------------------------------------------------------
RETCODE EXPORT
SymMapLinenum2FuncOrBlock(SYM_DESCRIPTOR module,
                          LINENUM_TYPE   linenum,
                          SYM_TYPE_TYPE  *symType,
                          SYM_DESCRIPTOR *outputSymbol)  {

   RETCODE            err;
   DESCRIPTOR         addrDesc;
   LINENUM_TYPE       actualLinenum;
   COLUMN_TYPE        actualColumn;
   LINENUM_DESCRIPTOR nextIndex;
   

   if (GOOD != (err = AdrCreateAddress(&addrDesc)))
      return err;

   // convert module and linenum to address
   if (GOOD != (err = SymGetLinenum(module,
                                           linenum,
                                           addrDesc,
                                           &actualLinenum,
                                           &actualColumn,
                                           &nextIndex)))
      return err;

   MEM_ADDR_CLASS memoryClass;
   U32            offset;
   SYM_DESCRIPTOR outSymbol;
   SYM_DESCRIPTOR funcDescriptor;
   SYM_DESCRIPTOR moduleDescriptor;

   // look up address for function or block that contains it
   if (GOOD != (err = SymMapAddr2Symbol(addrDesc,
                                               &memoryClass,
                                               symType,     //func input param
                                               &offset,
                                               &outSymbol,
                                               &funcDescriptor,
                                               &moduleDescriptor)))
      return err;

   *outputSymbol = outSymbol;  // copy the lowest-level descriptor to output
   // check for valid function or block type
   if (funcDescriptor == NULL_SYMBOL)
      return ER_SYMBOL_NOT_FOUND;

   return AdrDestroyAddress(addrDesc);
}  // end of SymMapLinenum2FuncOrBlock

/******************************** E O F ****************************/
