/* begincopyright
  Copyright (c) 1988 Xerox Corporation. All rights reserved.
  Use and copying of this software and preparation of derivative works based
  upon this software are permitted. Any distribution of this software or
  derivative works must comply with all applicable United States export
  control laws. This software is made available AS IS, and Xerox Corporation
  makes no warranty about the software, its performance or its conformity to
  any specification. Any person obtaining a copy of this software is requested
  to send their name and post office or electronic mail address to:
    PCR Coordinator
    Xerox PARC
    3333 Coyote Hill Rd.
    Palo Alto, CA 94304
  endcopyright */

/*
 * XR←IL.h
 *
 * Demers July 14, 1992
 */

/*
 *
 *   This interface provides dynamic loading of code into a PCR world,
 *   relocating the code, resolving external references, allocating
 *   common areas, etc.
 *
 *   Multiple object file formats are supported.  New object file readers
 *   may register themselves at any time -- see IncrementalLoadPrivate.h
 *   for details.
 *
 *   The incremental loader maintains a primitive *load state* consisting
 *   of a *file table* and a *symbol table*.
 *
 *   The file table holds information about individual files that have
 *   have been loaded: file name and type, address(es) where the file
 *   was loaded, etc.  Each file is loaded in up to 5 segments:
 *
 *     - text, data, bss: like standard Unix.
 *     - common: like bss, but allocated separately when common symbols get
 *       defined.
 *     - patch: area in which debugger can store short code sequences (e.g.
 *       for breakpoints); the architecture may require this to be "near"
 *       the text segment.
 *
 *   all these segment addresses appear in file table entries.
 *
 *   The symbol table holds information about all global symbols and all
 *   (local or global) procedure/label symbols (local procedure info is
 *   helpful for debugging and necessary for stack unwinding on certain
 *   processors).  Each symbol entry points to the file entry for the
 *   containing file. Symbols are accessible either by name or by value.
 *
 *   The data structures are designed to appear consistent at all times,
 *   so a debugger can freeze the PCR world and examine the load state.
 *   Normal clients (those that don't freeze the world and examine it
 *   from outside) need to follow a locking discipline:
 *
 *     call XR←LockIncrementalLoadState
 *     examine or update the load state
 *     call XR←UnlockIncrementalLoadState
 *
 *   The incremental loader maintains the invariant that there are
 *   no unresolved external references in the loaded code.  This
 *   could make it impossible to load several mutually-dependent
 *   object files, so the invariant is enforced only at *commit points*.
 *
 *   To load one or more object files, a client peforms the following
 *   sequence:
 *
 *     call XR←LockIncrementalLoadState
 *     call XR←ILLoadFile to load first file
 *       . . .
 *     call XR←ILLoadFile to load last file
 *     call XR←CommitIncrementalLoad
 *     call XR←UnlockIncrementalLoadState
 *
 *   The call to XR←CommitIncrementalLoad will fail if there are any
 *   undefined symbols.  If it fails, the client can either give up:
 *
 *     call XR←AbortIncrementalLoad
 *     call XR←UnlockIncrementalLoadState
 *
 *   or try to fix things by doing a library search:
 *
 *     while( calling XR←ILEnumerateUndefinedSyms produces symbols ) {
 *       find a library module that defines an undefined symbol 
 *       call XR←ILLoadFile to load it
 *     }
 *     call XR←CommitIncrementalLoad -- will succeed!
 *     call XR←UnlockIncrementalLoadState
 *
 */
 
#ifndef ←←XR←IL←h
#define ←←XR←IL←h

#include <xr/XR←Basics.h>
#include <xr/XR←sys←types.h>


/*
 * Error result returned from many IL calls.
 *
 * a NIL result means the call succeeded ...
 */

typedef struct XR←ILErrorRep {
    bool ile←fatal;	/* all incr loads since last commit are aborted */
    int ile←code;	/* > 0 ==> value from Errno.h */
    char *ile←msg;	/* human-readable */
} * XR←ILError;


/*
 * Incremental Load State Locking
 *
 * Data is guaranteed to be consistent only while lock is held.
 */

extern XR←ILError
XR←LockIncrementalLoadState(bool wait);
/*
    Try to acquire lock on load state.
    The `wait' parameter says whether to block until it's available.
    Waiting is abortable.
    Result.ile←code may be ETIMEDOUT or XR←EABORTED.

    A successful call must be followed (eventually) by a call to
      UnlockIncrementalLoadState.
*/

extern XR←ILError
XR←UnlockIncrementalLoadState(void);
/*
    Release lock on load state.
    Result.ile←code may be EINVAL if lock is not held.
*/


/*
 * Incremental Load File record
 */

typedef struct XR←ILFileEntryRep {

    /* incremental load info */
        unsigned ilfe←seqNum;
        bool ilfe←commitPoint;
    /* file info */
        char * ilfe←fName;
        unsigned ilfe←fOffset;
        unsigned ilfe←fMagic;
        XR←size←t ilfe←fSize;
        XR←time←t ilfe←fMTime;
    /* version stamp (optionally supplied by installation proc) */
        unsigned ilfe←vMagic;
        unsigned ilfe←vLen;
        char * ilfe←vStamp;
    /* file type specific data (optionally supplied by reader proc) */
        XR←caddr←t ilfe←rdrData;
        unsigned ilfe←rdrDataBytes;
    /* patch, text, data, bss, and common segments */
        XR←caddr←t ilfe←pAddr;
        unsigned ilfe←pBytes;
        XR←caddr←t ilfe←tAddr;
        unsigned ilfe←tBytes;
        XR←caddr←t ilfe←dAddr;
        unsigned ilfe←dBytes;
        XR←caddr←t ilfe←bAddr;
        unsigned ilfe←bBytes;
        XR←caddr←t ilfe←cAddr;
        unsigned ilfe←cBytes;

    void * ilfe←←under; /* -> internal data structure */

} *XR←ILFileEntry;


extern XR←ILFileEntry
XR←ILGetPrevFileEntry(XR←ILFileEntry ilfe);
/*
    Return ILFileEntries in reverse order of loading.
    Return most-recently-loaded entry if ilfe is NIL.
    Return NIL at end of sequence.
*/


extern void
XR←ILAddVersionStamp(
    XR←ILFileEntry ilfe,
    unsigned vMagic,
    unsigned vLen,
    XR←caddr←t vStamp
);

extern XR←ILFileEntry
XR←ILAddVersionStampUsingPC(
    XR←caddr←t pc,
    unsigned vMagic,
    unsigned vLen,
    char * vStamp
);
/*
    To be called by an installation proc to record its version stamp.

    The vStamp pointer is stored into the XR←ILFileEntry associated with pc,
      without being copied; thus, it can't point to a buffer in a local frame. 

    BEWARE: these procs don't do any locking.  Concurrent calls that add
      version stamps for different files don't interfere with one another.
*/


/*
 * Load state symbol table
 */

typedef struct XR←ILSymEntryRep {

    char *ilse←name;
    unsigned ilse←type;
    unsigned ilse←value;
    unsigned ilse←size;
    XR←ILFileEntry ilse←ilfe;

    void * ilse←←under; /* -> internal data structure */

} * XR←ILSymEntry;

/* ilse←type field encoding (may differ from a.out) */

#define	ILSE←UNDF	0x0		/* undefined */
#define	ILSE←ABS	0x2		/* absolute */
#define	ILSE←TEXT	0x4		/* text */
#define	ILSE←DATA	0x6		/* data */
#define	ILSE←BSS	0x8		/* bss */

#define ILSE←PATCH	0x1c		/* patch area address */
#define ILSE←MODULE	0x1e		/* module name */

#define	ILSE←EXT	01		/* external bit, or'ed in */
#define	ILSE←TYPE	0x1e		/* mask for all the type bits */


/*
 * Load state symbol lookup
 *
 * PCR retains all external or text symbols.
 * All these symbols are accessible by name.
 * Only relocatable (not absolute) symbols are accessible by value.
 */

/* wildcard code for wanted types */

#define WANT←ALL←TYPES	((unsigned)(-1))
	
/* codes for (un-)wanted classes */

#define IGNORE←NONE	0
#define IGNORE←INTERNAL	1
#define IGNORE←EXTERNAL	2


extern XR←ILSymEntry
XR←ILGetMatchingSymEntryByName(
    XR←ILSymEntry ilse,
    const char *pattern,
    bool caseSensitive,
    unsigned wantedTypes,
    unsigned ignoredClasses,
    int numToSkip
);
/*
    A legal pattern is either (1) a symbol or (2) a symbol containing at
      least one dot and followed by a star:
        foo, bar.o, baz.c2c.o, ... -- exact match.
        bar.*,  baz.c2c*, ... -- the star is a wildcard.
    Matching may be case-sensitive or not.
    Only symbol entries of the specified types and classes are considered. 

    Return XR←ILSymEntry for numToSkip'th next most recent definition of
      symbol matching pattern, assuming that ilse points to a symbol
      that matches the pattern.
    Return NIL if no more definitions.

    Useful special case: (ilse == NIL) and (numToSkip == 1) returns the most
      recent ILSymEntry matching the specification.

    If (ilse != NIL), then a NIL pat value will be interpreted as the
      name associated with ilse.

    Note the resulting ILSymEntry may be undefined (i.e. have no value) if
      it is in the symbol table because it has been referenced but not yet
      defined in the loadstate.
*/



extern XR←ILSymEntry
XR←ILGetMatchingSymEntryByValue(
    XR←ILSymEntry ilse,
    unsigned val,
    unsigned wantedTypes,
    unsigned ignoredClasses,
    int numToSkip
);
/*
    
    If ilse == NIL set it to the XR←ILSymEntry for most recently defined
      symbol of maximum value not greater than val, of wanted type and class.
    Then walk numToSkip entries in the symbol table -- walk towards larger
      values if (numToSkip > 0), smaller values if (numToSkip < 0) --
      and return the resulting XR←ILSymEntry.
    Return NIL if no such entry exists.
    Note: symbol will not be undefined.  Only text, module and external
      symbols are guaranteed to exist in the load state.
*/


/*
 * even more obsolete stuff
 */

extern XR←ILSymEntry
XR←ILLookupSymEntry(const char *sym, bool externOnly);
/*
    Case-sensitive symbol entry lookup.
    Return NIL on failure.
*/


extern XR←ILSymEntry
XR←ILGetPrevSymEntry(XR←ILSymEntry ilse, bool externOnly);
/*
    Next most recent version of symbol defined by ilse.
*/



/*
 *
 * Incremental Loading
 *
 */


/* Continue command to be returned from callbacks ... */

typedef char * XR←ILContinueAction;

#   define ilca←continue 	((XR←ILContinueAction)(0))
#   define ilca←dontBind	((XR←ILContinueAction)(1))
#   define ilca←doBind		((XR←ILContinueAction)(2))
#   define ilca←abort(msg)	((XR←ILContinueAction)(msg))

/* Utility for use from callbacks and elsewhere ... */

extern bool
XR←ILSymIsInRecentLoad(XR←ILSymEntry ilse);
/*
    Return TRUE iff ilse is a symbol in the most recent load sequence
      (which may be in progress)
*/


/* The incremental load procs themselves ... */



extern XR←ILError
XR←ILLoadFile(
    char *fName,
        /* file name; the string will be copied. */
    long fOffset,
        /* offset in file (for libraries). */
    unsigned fMagic,
        /* file magic number hint, or 0 => use heuristics */
    XR←ILContinueAction (*refProc)(
        char *sym,
        XR←ILSymEntry ilseOld,
        void *clientData
    ),
    void *refClientData,
        /* called for each sym that is undef external in file being loaded.
        // ilseOld != NIL if sym exists in load state.
        // returns:
        //   ilca←dontBind => create a new SymTabEntry of type UNDF|EXT.
        //   ilca←doBind => bind to existing SymTabEntry if one exists,
        //     else error.
        //   ilca←continue => bind to existing SymTabEntry if one exists,
        //     else create a new one of type UNDF|EXT.
        // refProc may be NIL; default action is ilca←continue. */
    XR←ILContinueAction (*defProc)(
        XR←ILSymEntry ilseNew,
        XR←ILSymEntry ilseOld,
        void *clientData
    ),
    void *defClientData,
        /* called for each symbol definition.
        // returns:
        //   ilca←dontBind => create new SymTabEntry with the given value
        //     and make any deferred relocation entries for old SymTabEntry
        //     refer to the new one instead.
        //   ilca←doBind => if existing SymTabEntry is undefined, define
        //     it with given value; else error.
        //   ilca←continue => if existing SymTabEntry is undefined, define
        //     it with given value; else create new SymTabEntry with the
        //     given value and make any deferred relocation entries for
        //     the old SymTabEntry refer to the new one instead.
        // defProc may be NIL; default action is continue. */
    XR←ILContinueAction (*commonProc)(
        char *sym,
        unsigned size,
        XR←ILSymEntry ilseOld,
        void *clientData
    ),
    void *commonClientData,
        /* called for each common reference from current file.
        // returns:
        //   ilca←dontBind => create new common SymTabEntry.
        //   ilca←doBind => if existing SymTabEntry is defined, bind to
        //     it ignoring the size specified for the common symbol;
        //     if existing SymTabEntry is common, max the specified size
        //     with its size; else error.
        //   ilca←continue => if existing SymTabEntry is defined, bind to
        //     it ignoring the size specified for the common symbol;
        //     if existing SymTabEntry is common, max the specified size
        //     with its size; else create new common SymTabEntry.
        // commonProc may be NIL, default action is continue. */
    void (*patchSizeProc)(
        XR←ILFileEntry ilfe,
        void *clientData
    ),
    void *patchSizeClientData
        /* called with ilfe before allocating space for the
        // segments.  May look at segment sizes, and update
        // patch area size if desired. */
);
/*
    Load the specified module.
    Resolve external symbols from current load state.
    WARNING: DO NOT CALL THIS PROC RECURSIVELY
        E.G. FROM undefinedProc OR redefinedProc!
    Return NIL on success.
      (XR←ILFileEntry for the file just loaded is XR←ILGetPrevFileEntry(NIL))
*/


extern void
XR←ILEnumerateUndefinedSyms(
    bool (*func)(XR←ILSymEntry ilse, void *clientData),
        /* return TRUE iff enumeration should continue */
    void *clientData 
);
/*
    Enumerate all undefined symbols.
    (Note: all such symbols must have been introduced since last commit.)
    THIS WORKS ONLY AFTER A FAILED COMMIT.
*/


extern XR←ILError
XR←CommitIncrementalLoad(void);
/*
    Commit all incremental loads since last commit.
    Fail if there are any remaining undefined symbols.
    Do not release lock.
*/


extern XR←ILError
XR←AbortIncrementalLoad(XR←ILSymEntry ilse);
/*
    Abort all incremental loads since last commit (if ilse == NIL)
      or since the most recent commit before ilse was loaded.
*/


extern XR←ILError
XR←InitializeIncrementalLoader(char *fName);
/*
    Initialize loadstate symbol tables for PCR itself; fName specifies
      the PCR object file.  fName may be NIL.
    CALL EXACTLY ONCE FROM FIRST THREAD AT BEGINNING OF WORLD.
*/

#endif /* ←←XR←IL←h */