/* import-heap.c
 *
 * COPYRIGHT (c) 1992 by AT&T Bell Laboratories.
 *
 * Routines to import an ML heap image.
 */

#include <stdio.h>
#include <string.h>
#include "ml-base.h"
#include "machine-id.h"
#include "memory.h"
#include "cache-flush.h"
#include "ml-state.h"
#include "ml-values.h"
#include "ml-objects.h"
#include "ml-globals.h"
#include "card-map.h"
#include "heap.h"
#include "ml-heap-image.h"
#include "c-globals-tbl.h"
#include "addr-hash.h"
#include "heap-input.h"
#include "heap-io.h"

#if defined(DLOPEN) && !defined(OPSYS_WIN32)
#include <dlfcn.h>
#endif

#ifdef DEBUG
PVT void PrintRegionMap (bo_region_reloc_t *r)
{
    bo_reloc_t		*dp, *dq;
    int			i;

    SayDebug ("region @%#x: |", r->firstPage);
    for (i = 0, dq = r->objMap[0];  i < r->nPages;  i++) {
	dp = r->objMap[i];
	if (dp != dq) {
	    SayDebug ("|");
	    dq = dp;
	}
	if (dp == NIL(bo_reloc_t *))
	    SayDebug ("_");
	else
	    SayDebug ("X");
    }
    SayDebug ("|\n");

} /* end of PrintRegionMap */
#endif


/* local routines */
PVT void ReadHeap (inbuf_t *bp, ml_heap_hdr_t *hdr, ml_state_t *msp, ml_val_t *externs);
PVT bigobj_desc_t *AllocBODesc (bigobj_desc_t *, bigobj_hdr_t *, bo_region_reloc_t *);
PVT void RepairHeap (
	heap_t *, aid_t *, Addr_t [MAX_NUM_GENS][NUM_ARENAS],
	addr_tbl_t *, ml_val_t *);
PVT ml_val_t RepairWord (
	ml_val_t w, aid_t *oldBIBOP, Addr_t addrOffset[MAX_NUM_GENS][NUM_ARENAS],
	addr_tbl_t *boRegionTbl, ml_val_t *externs);
/*
PVT int RepairBORef (aid_t *bibop, aid_t id, ml_val_t *ref, ml_val_t oldObj);
*/
PVT bo_reloc_t *AddrToRelocInfo (aid_t *, addr_tbl_t *, aid_t, Addr_t);

#define READ(bp,obj)	HeapIO_ReadBlock(bp, &(obj), sizeof(obj))


/* ImportHeapImage:
 */
ml_state_t *ImportHeapImage (const char *fname, heap_params_t *params)
{
    ml_state_t		*msp;
    ml_image_hdr_t	imHdr;
    ml_heap_hdr_t	heapHdr;
    ml_val_t		*externs;
    ml_vproc_image_t	image;
    inbuf_t		inBuf;

    if (fname != NULL) {
      /* Resolve the name of the image.  If the file exists use it, otherwise try the
       * pathname with the machine ID as an extension.
       */
      if ((inBuf.file = fopen(fname, "rb")) != NULL) {
	if (! SilentLoad)
	  Say("loading %s ", fname);
      }
      else {
	char	buf[1024];

	if (QualifyImageName(strcpy(buf, fname))
	    && ((inBuf.file = fopen(buf, "rb")) != NULL)) {
	  if (! SilentLoad)
	    Say("loading %s ", buf);
	}
	else
	  Die ("unable to open heap image \"%s\"\n", fname);
      }

      inBuf.needsSwap = FALSE;
      inBuf.buf	    = NIL(Byte_t *);
      inBuf.nbytes    = 0;
    } else {
      /* fname == NULL, so try to find an in-core heap image */
#if defined(DLOPEN) && !defined(OPSYS_WIN32)
      void *lib = dlopen (NULL, RTLD_LAZY);
      void *vimg, *vimglenptr;
      if ((vimg = dlsym(lib,HEAP_IMAGE_SYMBOL)) == NULL)
	Die("no in-core heap image found\n");
      if ((vimglenptr = dlsym(lib,HEAP_IMAGE_LEN_SYMBOL)) == NULL)
	Die("unable to find length of in-core heap image\n");

      inBuf.file = NULL;
      inBuf.needsSwap = FALSE;
      inBuf.base = vimg;
      inBuf.buf = inBuf.base;
      inBuf.nbytes = *(long*)vimglenptr;
#else
      Die("in-core heap images not implemented\n");
#endif
    }

    READ(&inBuf, imHdr);
    if (imHdr.byteOrder != ORDER)
	Die ("incorrect byte order in heap image\n");
    if (imHdr.magic != IMAGE_MAGIC)
	Die ("bad magic number (%#x) in heap image\n", imHdr.magic);
    if ((imHdr.kind != EXPORT_HEAP_IMAGE) && (imHdr.kind != EXPORT_FN_IMAGE))
	Die ("bad image kind (%d) in heap image\n", imHdr.kind);
    READ(&inBuf, heapHdr);

  /* check for command-line overrides of heap parameters. */
    if (params->allocSz == 0) params->allocSz = heapHdr.allocSzB;
    if (params->numGens < heapHdr.numGens) params->numGens = heapHdr.numGens;
    if (params->cacheGen < 0) params->cacheGen = heapHdr.cacheGen;

    msp = AllocMLState (FALSE, params);

  /* get the run-time pointers into the heap */
    *PTR_MLtoC(ml_val_t, PervStruct) = heapHdr.pervStruct;
    RunTimeCompUnit = heapHdr.runTimeCompUnit;
#ifdef ASM_MATH
    MathVec = heapHdr.mathVec;
#endif

  /* read the externals table */
    externs = HeapIO_ReadExterns (&inBuf);

  /* read and initialize the ML state info */
    READ(&inBuf, image);
    if (imHdr.kind == EXPORT_HEAP_IMAGE) {
      /* Load the live registers */
	ASSIGN(MLSignalHandler, image.sigHandler);
	msp->ml_arg		= image.stdArg;
	msp->ml_cont		= image.stdCont;
	msp->ml_closure		= image.stdClos;
	msp->ml_pc		= image.pc;
	msp->ml_exnCont		= image.exnCont;
	msp->ml_varReg		= image.varReg;
	msp->ml_calleeSave[0]	= image.calleeSave[0];
	msp->ml_calleeSave[1]	= image.calleeSave[1];
	msp->ml_calleeSave[2]	= image.calleeSave[2];
      /* read the ML heap */
	ReadHeap (&inBuf, &heapHdr, msp, externs);
      /* GC message are on by default for interactive images */
      /* GCMessages = TRUE; */
    }
    else {  /* EXPORT_FN_IMAGE */
	ml_val_t	funct, cmdName, args;
      /* restore the signal handler */
	ASSIGN(MLSignalHandler, image.sigHandler);
      /* read the ML heap */
	msp->ml_arg		= image.stdArg;
	ReadHeap (&inBuf, &heapHdr, msp, externs);
      /* initialize the calling context (taken from ApplyMLFn) */
	funct			= msp->ml_arg;
	msp->ml_exnCont		= PTR_CtoML(handle_v+1);
	msp->ml_varReg		= ML_unit;
	msp->ml_cont		= PTR_CtoML(return_c);
	msp->ml_closure		= funct;
	msp->ml_pc		=
	msp->ml_linkReg		= GET_CODE_ADDR(funct);
      /* setup the arguments to the imported function */
	cmdName = ML_CString(msp, MLCmdName);
	args = ML_CStringList (msp, CmdLineArgs);
	REC_ALLOC2(msp, msp->ml_arg, cmdName, args);
/*
SayDebug("arg = %#x : [%#x, %#x]\n", msp->ml_arg, REC_SEL(msp->ml_arg, 0), REC_SEL(msp->ml_arg, 1));
*/
      /* GC message are off by default for exportFn images */
	GCMessages = FALSE;
    }

    FREE (externs);
    if (inBuf.file != NULL)
      fclose (inBuf.file);

    if (! SilentLoad)
	Say(" done\n");

    return msp;

} /* end of ImportHeapImage */

/* ReadHeap:
 */
PVT void ReadHeap (inbuf_t *bp, ml_heap_hdr_t *hdr, ml_state_t *msp, ml_val_t *externs)
{
    heap_t		*heap = msp->ml_heap;
    heap_arena_hdr_t	*arenaHdrs, *p, *q;
    int			arenaHdrsSize;
    int			i, j, k;
    long		prevSzB[NUM_ARENAS], sz;
    bibop_t		oldBIBOP;
    Addr_t		addrOffset[MAX_NUM_GENS][NUM_ARENAS];
    bo_region_reloc_t	*boRelocInfo;
    addr_tbl_t		*boRegionTbl;

  /* Allocate a BIBOP for the imported heap image's address space. */
#ifdef TWO_LEVEL_MAP
#  error two level map not supported
#else
    oldBIBOP = NEW_VEC (aid_t, BIBOP_SZ);
#endif

  /* read in the big-object region descriptors for the old address space */
    {
	int			sz;
	bo_region_info_t	*boRgnHdr;

	boRegionTbl = MakeAddrTbl(BIBOP_SHIFT+1, hdr->numBORegions);
	sz = hdr->numBORegions * sizeof(bo_region_info_t);
	boRgnHdr = (bo_region_info_t *) MALLOC (sz);
	HeapIO_ReadBlock (bp, boRgnHdr, sz);

	boRelocInfo = NEW_VEC(bo_region_reloc_t, hdr->numBORegions);
	for (i = 0;  i < hdr->numBORegions;  i++) {
	    MarkRegion(oldBIBOP,
		(ml_val_t *)(boRgnHdr[i].baseAddr),
		RND_MEMOBJ_SZB(boRgnHdr[i].sizeB),
		AID_BIGOBJ(1));
	    oldBIBOP[BIBOP_ADDR_TO_INDEX(boRgnHdr[i].baseAddr)] = AID_BIGOBJ_HDR(MAX_NUM_GENS);
	    boRelocInfo[i].firstPage = boRgnHdr[i].firstPage;
	    boRelocInfo[i].nPages =
		(boRgnHdr[i].sizeB - (boRgnHdr[i].firstPage - boRgnHdr[i].baseAddr))
		    >> BIGOBJ_PAGE_SHIFT;
	    boRelocInfo[i].objMap = NEW_VEC(bo_reloc_t *, boRelocInfo[i].nPages);
	    for (j = 0;  j < boRelocInfo[i].nPages;  j++)
		boRelocInfo[i].objMap[j] = NIL(bo_reloc_t *);
	    AddrTblInsert (boRegionTbl, boRgnHdr[i].baseAddr, &(boRelocInfo[i]));
	}
	FREE (boRgnHdr);
    }

  /* read the arena headers. */
    arenaHdrsSize = hdr->numGens * NUM_OBJ_KINDS * sizeof(heap_arena_hdr_t);
    arenaHdrs = (heap_arena_hdr_t *) MALLOC (arenaHdrsSize);
    HeapIO_ReadBlock (bp, arenaHdrs, arenaHdrsSize);

    for (i = 0;  i < NUM_ARENAS;  i++)
	prevSzB[i] = heap->allocSzB;

  /* allocate the arenas and read in the heap image. */
    for (p = arenaHdrs, i = 0;  i < hdr->numGens;  i++) {
	gen_t	*gen = heap->gen[i];

      /* compute the space required for this generation, and mark the oldBIBOP
       * to reflect the old address space.
       */
	for (q = p, j = 0;  j < NUM_ARENAS;  j++) {
	    MarkRegion (oldBIBOP,
		(ml_val_t *)(q->info.o.baseAddr),
		RND_MEMOBJ_SZB(q->info.o.sizeB),
		gen->arena[j]->id);
	    sz = q->info.o.sizeB + prevSzB[j];
	    if ((j == PAIR_INDX) && (sz > 0))
		sz += 2*WORD_SZB;
	    gen->arena[j]->tospSizeB = RND_MEMOBJ_SZB(sz);
	    prevSzB[j] = q->info.o.sizeB;
	    q++;
	}

      /* Allocate space for the generation */
	if (NewGeneration(gen) == FAILURE)
	    Die ("unable to allocated space for generation %d\n", i+1);
	if (isACTIVE(gen->arena[ARRAY_INDX]))
	    NewDirtyVector (gen);

      /* read in the arenas for this generation and initialize the
       * address offset table.
       */
	for (j = 0;  j < NUM_ARENAS;  j++) {
	    arena_t		*ap = gen->arena[j];

	    if (p->info.o.sizeB > 0) {
		addrOffset[i][j] = (Addr_t)(ap->tospBase) - (Addr_t)(p->info.o.baseAddr);
		HeapIO_Seek (bp, (long)(p->offset));
		HeapIO_ReadBlock(bp, (ap->tospBase), p->info.o.sizeB);
		ap->nextw	= (ml_val_t *)((Addr_t)(ap->tospBase) + p->info.o.sizeB);
		ap->oldTop	= ap->tospBase;
	    }
	    else if (isACTIVE(ap)) {
		ap->oldTop = ap->tospBase;
	    }
	    if (! SilentLoad) {
		Say(".");
	    }
	    p++;
	}
      /* read in the big-object arenas */
	for (j = 0;  j < NUM_BIGOBJ_KINDS;  j++) {
	    Addr_t		totSizeB;
	    bigobj_desc_t	*freeObj, *bdp;
	    bigobj_region_t	*freeRegion;
	    bigobj_hdr_t	*boHdrs;
	    int			boHdrSizeB, indx;
	    bo_region_reloc_t   *region;

	    if (p->info.bo.numBOPages > 0) {
		totSizeB = p->info.bo.numBOPages << BIGOBJ_PAGE_SHIFT;
		freeObj = BO_AllocRegion (heap, totSizeB);
		freeRegion = freeObj->region;
		freeRegion->minGen = i;
		MarkRegion (BIBOP, (ml_val_t *)freeRegion,
		    MEMOBJ_SZB(freeRegion->memObj), AID_BIGOBJ(i));
		BIBOP[BIBOP_ADDR_TO_INDEX(freeRegion)] = AID_BIGOBJ_HDR(i);

	      /* read in the big-object headers */
		boHdrSizeB = p->info.bo.numBigObjs * sizeof(bigobj_hdr_t);
		boHdrs = (bigobj_hdr_t *) MALLOC (boHdrSizeB);
		HeapIO_ReadBlock (bp, boHdrs, boHdrSizeB);

	      /* read in the big-objects */
		HeapIO_ReadBlock (bp, (void *)(freeObj->obj), totSizeB);
		if (j == CODE_INDX) {
		    FlushICache ((void *)(freeObj->obj), totSizeB);
		}

	      /* setup the big-object descriptors and per-object relocation info */
		for (k = 0;  k < p->info.bo.numBigObjs;  k++) {
		  /* find the region relocation info for the object's region in
		   * the exported heap.
		   */
		    for (indx = BIBOP_ADDR_TO_INDEX(boHdrs[k].baseAddr);
			!BO_IS_HDR(oldBIBOP[indx]);
			indx--)
			continue;
		    region = LookupBORegion (boRegionTbl, indx);
		  /* allocate the big-object descriptor for the object, and
		   * link it into the list of big-objects for its generation.
		   */
		    bdp = AllocBODesc (freeObj, &(boHdrs[k]), region);
		    bdp->next = gen->bigObjs[j];
		    gen->bigObjs[j] = bdp;
		    ASSERT(bdp->gen == i+1);

		    if (DumpObjectStrings && (j == CODE_INDX)) {
		      /* dump the comment string of the code object */
			char           *namestring;
			if ((namestring = BO_GetCodeObjTag(bdp)) != NIL(char *))
			    SayDebug ("[%6d bytes] %s\n", bdp->sizeB, namestring);
		    }
		}

		if (freeObj != bdp) {
		  /* there was some extra space left in the region */
		    ADD_BODESC(heap->freeBigObjs, freeObj);
		}

		FREE (boHdrs);
	    }
	    if (! SilentLoad) {
		Say(".");
	    }
	    p++;
	}
    }

    RepairHeap (heap, oldBIBOP, addrOffset, boRegionTbl, externs);

  /* Adjust the run-time globals that point into the heap */
    *PTR_MLtoC(ml_val_t, PervStruct) = RepairWord (
	*PTR_MLtoC(ml_val_t, PervStruct),
	oldBIBOP, addrOffset, boRegionTbl, externs);
    RunTimeCompUnit = RepairWord (
	RunTimeCompUnit, oldBIBOP, addrOffset, boRegionTbl, externs);
#ifdef ASM_MATH
    MathVec = RepairWord (MathVec, oldBIBOP, addrOffset, boRegionTbl, externs);
#endif

  /* Adjust the ML registers to the new address space */
    ASSIGN(MLSignalHandler, RepairWord (
	DEREF(MLSignalHandler), oldBIBOP, addrOffset, boRegionTbl, externs));
    msp->ml_arg = RepairWord (
        msp->ml_arg, oldBIBOP, addrOffset, boRegionTbl, externs);
    msp->ml_cont = RepairWord (
        msp->ml_cont, oldBIBOP, addrOffset, boRegionTbl, externs);
    msp->ml_closure = RepairWord (
        msp->ml_closure, oldBIBOP, addrOffset, boRegionTbl, externs);
    msp->ml_pc = RepairWord (
        msp->ml_pc, oldBIBOP, addrOffset, boRegionTbl, externs);
    msp->ml_linkReg = RepairWord (
        msp->ml_linkReg, oldBIBOP, addrOffset, boRegionTbl, externs);
    msp->ml_exnCont = RepairWord (
        msp->ml_exnCont, oldBIBOP, addrOffset, boRegionTbl, externs);
    msp->ml_varReg = RepairWord (
        msp->ml_varReg, oldBIBOP, addrOffset, boRegionTbl, externs);
    msp->ml_calleeSave[0] = RepairWord (
        msp->ml_calleeSave[0], oldBIBOP, addrOffset, boRegionTbl, externs);
    msp->ml_calleeSave[1] = RepairWord (
        msp->ml_calleeSave[1], oldBIBOP, addrOffset, boRegionTbl, externs);
    msp->ml_calleeSave[2] = RepairWord (
        msp->ml_calleeSave[2], oldBIBOP, addrOffset, boRegionTbl, externs);

  /* release storage */
    for (i = 0; i < hdr->numBORegions;  i++) {
	bo_reloc_t	*p;
	for (p = NIL(bo_reloc_t *), j = 0;  j < boRelocInfo[i].nPages;  j++) {
	    if ((boRelocInfo[i].objMap[j] != NIL(bo_reloc_t *))
	    && (boRelocInfo[i].objMap[j] != p)) {
		FREE (boRelocInfo[i].objMap[j]);
		p = boRelocInfo[i].objMap[j];
	    }
	}
    }
    FreeAddrTbl (boRegionTbl, FALSE);
    FREE (boRelocInfo);
    FREE (arenaHdrs);
    FREE (oldBIBOP);

  /* reset the sweep_nextw pointers */
    for (i = 0;  i < heap->numGens;  i++) {
	gen_t	*gen = heap->gen[i];
	for (j = 0;  j < NUM_ARENAS;  j++) {
	    arena_t		*ap = gen->arena[j];
	    if (isACTIVE(ap))
		ap->sweep_nextw = ap->nextw;
	}
    }

} /* end of ReadHeap. */

/* AllocBODesc:
 *
 */
PVT bigobj_desc_t *AllocBODesc (
    bigobj_desc_t   *free,
    bigobj_hdr_t    *objHdr,
    bo_region_reloc_t *oldRegion)
{
    bigobj_region_t *region;
    bigobj_desc_t   *newObj;
    bo_reloc_t	    *relocInfo;
    int		    i, totSzB, firstPage, npages;

    totSzB = ROUNDUP(objHdr->sizeB, BIGOBJ_PAGE_SZB);
    npages = (totSzB >> BIGOBJ_PAGE_SHIFT);
    region = free->region;
    if (free->sizeB == totSzB)
      /* allocate the whole free area to the object */
	newObj = free;
    else {
      /* split the free object */
	newObj		= NEW_OBJ(bigobj_desc_t);
	newObj->obj	= free->obj;
	newObj->region	= region;
	free->obj	= (Addr_t)(free->obj) + totSzB;
	free->sizeB	-= totSzB;
	firstPage	= ADDR_TO_BOPAGE(region, newObj->obj);
	for (i = 0;  i < npages;  i++)
	    region->objMap[firstPage+i] = newObj;
    }

    newObj->sizeB	= objHdr->sizeB;
    newObj->state	= BO_YOUNG;
    newObj->gen		= objHdr->gen;
    newObj->objc	= objHdr->objKind;
    region->nFree	-= npages;

  /* setup the relocation info */
    relocInfo = NEW_OBJ(bo_reloc_t);
    relocInfo->oldAddr = objHdr->baseAddr;
    relocInfo->newObj = newObj;
    firstPage = ADDR_TO_BOPAGE(oldRegion, objHdr->baseAddr);
    for (i = 0;  i < npages;  i++)
	oldRegion->objMap[firstPage+i] = relocInfo;

    return newObj;

} /* end of AllocBODesc */

/* RepairHeap:
 *
 * Scan the heap, replacing external references with their addresses and
 * adjusting pointers.
 */
PVT void RepairHeap (
    heap_t *heap,
    aid_t *oldBIBOP,
    Addr_t addrOffset[MAX_NUM_GENS][NUM_ARENAS],
    addr_tbl_t *boRegionTbl,
    ml_val_t *externs)
{
    int		i;

    for (i = 0;  i < heap->numGens;  i++) {
	gen_t	*gen = heap->gen[i];
#ifndef BIT_CARDS
#define MARK(cm, p, g)	MARK_CARD(cm, p, g)
#else
#define MARK(cm, p, g)	MARK_CARD(cm, p)
#endif
#define RepairArena(indx)	{						\
	    arena_t		*__ap = gen->arena[(indx)];			\
	    ml_val_t	*__p, *__q;						\
	    __p = __ap->tospBase;						\
	    __q = __ap->nextw;							\
	    while (__p < __q) {							\
		ml_val_t	__w = *__p;					\
		int		__gg, __objc;					\
		if (isBOXED(__w)) {						\
		    Addr_t	__obj = PTR_MLtoADDR(__w);			\
		    aid_t	__aid = ADDR_TO_PAGEID(oldBIBOP, __obj);	\
		    if (IS_BIGOBJ_AID(__aid)) {					\
			bo_reloc_t	*__dp;					\
			__dp = AddrToRelocInfo (oldBIBOP, boRegionTbl,		\
				__aid, __obj);					\
			*__p = PTR_CtoML((__obj - __dp->oldAddr) 		\
				+ __dp->newObj->obj);				\
			__gg = __dp->newObj->gen-1;				\
		    }								\
		    else {							\
			__gg = EXTRACT_GEN(__aid)-1;				\
			__objc = EXTRACT_OBJC(__aid)-1;				\
			*__p = PTR_CtoML(__obj + addrOffset[__gg][__objc]);	\
		    }								\
		    if (((indx) == ARRAY_INDX) && (__gg < i)) {			\
			MARK(gen->dirty, __p, __gg+1);	/** **/			\
		    }								\
		}								\
		else if (isEXTERNTAG(__w)) {					\
		    *__p = externs[EXTERNID(__w)];				\
		}								\
		__p++;								\
	    }									\
	} /* RepairArena */

	RepairArena(RECORD_INDX);
	RepairArena(PAIR_INDX);
	RepairArena(ARRAY_INDX);
    }

} /* end of RepairHeap */

/* RepairWord:
 */
PVT ml_val_t RepairWord (
    ml_val_t w,
    aid_t *oldBIBOP,
    Addr_t addrOffset[MAX_NUM_GENS][NUM_ARENAS],
    addr_tbl_t *boRegionTbl,
    ml_val_t *externs)
{
    if (isBOXED(w)) {
	Addr_t	obj = PTR_MLtoADDR(w);
	aid_t	aid = ADDR_TO_PAGEID(oldBIBOP, obj);
	if (IS_BIGOBJ_AID(aid)) {
	    bo_reloc_t	*dp;
	    dp = AddrToRelocInfo (oldBIBOP, boRegionTbl, aid, obj);
	    return PTR_CtoML((obj - dp->oldAddr) + dp->newObj->obj);
	}
	else {
	    int	g = EXTRACT_GEN(aid)-1;
	    int	objc = EXTRACT_OBJC(aid)-1;
	    return PTR_CtoML(PTR_MLtoC(char, w) + addrOffset[g][objc]);
	}
    }
    else if (isEXTERNTAG(w)) {
	return externs[EXTERNID(w)];
    }
    else
	return w;

} /* end of RepairWord */


/* AddrToRelocInfo:
 */
PVT bo_reloc_t *AddrToRelocInfo (
    aid_t *oldBIBOP, 
    addr_tbl_t *boRegionTbl, 
    aid_t id, 
    Addr_t oldObj)
{
    int		    indx;
    bo_region_reloc_t *region;

    for (indx = BIBOP_ADDR_TO_INDEX(oldObj);  !BO_IS_HDR(id);  id = oldBIBOP[--indx])
	continue;

  /* find the old region descriptor */
    region = LookupBORegion (boRegionTbl, indx);

    if (region == NIL(bo_region_reloc_t *))
	Die ("unable to map big-object @ %#x; index = %#x, id = %#x\n",
	    oldObj, indx, (unsigned)id);

    return ADDR_TO_BODESC(region, oldObj);

} /* end of AddrToRelocInfo */
