1/*
2    Title:     Export and import memory in a portable format
3    Author:    David C. J. Matthews.
4
5    Copyright (c) 2006-7, 2015-7 David C. J. Matthews
6
7
8    This library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public
10    License version 2.1 as published by the Free Software Foundation.
11
12    This library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR H PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with this library; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20*/
21#ifdef HAVE_CONFIG_H
22#include "config.h"
23#elif defined(_WIN32)
24#include "winconfig.h"
25#else
26#error "No configuration file"
27#endif
28
29#ifdef HAVE_STDIO_H
30#include <stdio.h>
31#endif
32
33#ifdef HAVE_ERRNO_H
34#include <errno.h>
35#endif
36
37#ifdef HAVE_ASSERT_H
38#include <assert.h>
39#define ASSERT(x) assert(x)
40#else
41#define ASSERT(x)
42#endif
43
44#include "globals.h"
45#include "pexport.h"
46#include "machine_dep.h"
47#include "scanaddrs.h"
48#include "run_time.h"
49#include "../polyexports.h"
50#include "version.h"
51#include "sys.h"
52#include "polystring.h"
53#include "processes.h" // For IO_SPACING
54#include "memmgr.h"
55#include "osmem.h"
56#include "rtsentry.h"
57
58/*
59This file contains the code both to export the file and to import it
60in a new session.
61*/
62
63PExport::PExport()
64{
65}
66
67PExport::~PExport()
68{
69}
70
71
72// Get the index corresponding to an address.
73size_t PExport::getIndex(PolyObject *p)
74{
75    // Binary chop to find the index from the address.
76    size_t lower = 0, upper = pMap.size();
77    while (1)
78    {
79        ASSERT(lower < upper);
80        size_t middle = (lower+upper)/2;
81        ASSERT(middle < pMap.size());
82        if (p < pMap[middle])
83        {
84            // Use lower to middle
85            upper = middle;
86        }
87        else if (p > pMap[middle])
88        {
89            // Use middle+1 to upper
90            lower = middle+1;
91        }
92        else // Found it
93            return middle;
94    }
95}
96
97/* Get the index corresponding to an address. */
98void PExport::printAddress(void *p)
99{
100    fprintf(exportFile, "@%" PRI_SIZET "", getIndex((PolyObject*)p));
101}
102
103void PExport::printValue(PolyWord q)
104{
105    if (IS_INT(q) || q == PolyWord::FromUnsigned(0))
106        fprintf(exportFile, "%" POLYSFMT, UNTAGGED(q));
107    else
108        printAddress(q.AsAddress());
109}
110
111void PExport::printObject(PolyObject *p)
112{
113    POLYUNSIGNED length = p->Length();
114    POLYUNSIGNED i;
115
116    size_t myIndex = getIndex(p);
117
118    fprintf(exportFile, "%" PRI_SIZET ":", myIndex);
119
120    if (p->IsMutable())
121        putc('M', exportFile);
122    if (OBJ_IS_NEGATIVE(p->LengthWord()))
123        putc('N', exportFile);
124    if (OBJ_IS_WEAKREF_OBJECT(p->LengthWord()))
125        putc('W', exportFile);
126    if (OBJ_IS_NO_OVERWRITE(p->LengthWord()))
127        putc('V', exportFile);
128
129    if (p->IsByteObject())
130    {
131        if (p->IsMutable() && p->IsWeakRefObject())
132        {
133            // This is either an entry point or a weak ref used in the FFI.
134            // Clear the first word
135            if (p->Length() >= 1) p->Set(0, PolyWord::FromSigned(0));
136        }
137        /* May be a string, a long format arbitrary precision
138           number or a real number. */
139        PolyStringObject* ps = (PolyStringObject*)p;
140        /* This is not infallible but it seems to be good enough
141           to detect the strings. */
142        POLYUNSIGNED bytes = length * sizeof(PolyWord);
143        if (length >= 2 &&
144            ps->length <= bytes - sizeof(POLYUNSIGNED) &&
145            ps->length > bytes - 2 * sizeof(POLYUNSIGNED))
146        {
147            /* Looks like a string. */
148            fprintf(exportFile, "S%" POLYUFMT "|", ps->length);
149            for (unsigned i = 0; i < ps->length; i++)
150            {
151                char ch = ps->chars[i];
152                fprintf(exportFile, "%02x", ch & 0xff);
153            }
154        }
155        else
156        {
157            /* Not a string. May be an arbitrary precision integer.
158               If the source and destination word lengths differ we
159               could find that some long-format arbitrary precision
160               numbers could be represented in the tagged short form
161               or vice-versa.  The former case might give rise to
162               errors because when comparing two arbitrary precision
163               numbers for equality we assume that they are not equal
164               if they have different representation.  The latter
165               case could be a problem because we wouldn't know whether
166               to convert the tagged form to long form, which would be
167               correct if the value has type "int" or to truncate it
168               which would be correct for "word".
169               It could also be a real number but that doesn't matter
170               if we recompile everything on the new machine.
171            */
172            byte *u = (byte*)p;
173            putc('B', exportFile);
174            fprintf(exportFile, "%" POLYUFMT "|", length*sizeof(PolyWord));
175            for (unsigned i = 0; i < (unsigned)(length*sizeof(PolyWord)); i++)
176            {
177                fprintf(exportFile, "%02x", u[i]);
178            }
179        }
180    }
181    else if (p->IsCodeObject())
182    {
183        POLYUNSIGNED constCount, i;
184        PolyWord *cp;
185        ASSERT(! p->IsMutable() );
186        /* Work out the number of bytes in the code and the
187           number of constants. */
188        p->GetConstSegmentForCode(cp, constCount);
189        /* The byte count is the length of the segment minus the
190           number of constants minus one for the constant count.
191           It includes the marker word, byte count, profile count
192           and, on the X86/64 at least, any non-address constants.
193           These are actually word values. */
194        POLYUNSIGNED byteCount = (length - constCount - 1) * sizeof(PolyWord);
195        fprintf(exportFile, "D%" POLYUFMT ",%" POLYUFMT "|", constCount, byteCount);
196
197        // First the code.
198        byte *u = (byte*)p;
199        for (i = 0; i < byteCount; i++)
200            fprintf(exportFile, "%02x", u[i]);
201
202        putc('|', exportFile);
203        // Now the constants.
204        for (i = 0; i < constCount; i++)
205        {
206            printValue(cp[i]);
207            if (i < constCount-1)
208                putc(',', exportFile);
209        }
210        putc('|', exportFile);
211        // Finally any constants in the code object.
212        machineDependent->ScanConstantsWithinCode(p, this);
213    }
214    else /* Ordinary objects, essentially tuples. */
215    {
216        fprintf(exportFile, "O%" POLYUFMT "|", length);
217        for (i = 0; i < length; i++)
218        {
219            printValue(p->Get(i));
220            if (i < length-1)
221                putc(',', exportFile);
222        }
223    }
224    fprintf(exportFile, "\n");
225}
226
227/* This is called for each constant within the code.
228   Print a relocation entry for the word and return a value that means
229   that the offset is saved in original word. */
230void PExport::ScanConstant(PolyObject *base, byte *addr, ScanRelocationKind code)
231{
232    PolyWord p = GetConstantValue(addr, code);
233    // We put in all the values including tagged constants.
234    // Put in the byte offset and the relocation type code.
235    POLYUNSIGNED offset = (POLYUNSIGNED)(addr - (byte*)base);
236    ASSERT (offset < base->Length() * sizeof(POLYUNSIGNED));
237    fprintf(exportFile, "%" POLYUFMT ",%d,", (POLYUNSIGNED)(addr - (byte*)base), code);
238    printValue(p); // The value to plug in.
239    fprintf(exportFile, " ");
240}
241
242void PExport::exportStore(void)
243{
244    // We want the entries in pMap to be in ascending
245    // order of address to make searching easy so we need to process the areas
246    // in order of increasing address, which may not be the order in memTable.
247    std::vector<size_t> indexOrder;
248    indexOrder.reserve(memTableEntries);
249
250    for (size_t i = 0; i < memTableEntries; i++)
251    {
252        std::vector<size_t>::iterator it;
253        for (it = indexOrder.begin(); it != indexOrder.end(); it++) {
254            if (memTable[*it].mtAddr >= memTable[i].mtAddr)
255                break;
256        }
257        indexOrder.insert(it, i);
258    }
259
260    // Process the area in order of ascending address.
261    for (std::vector<size_t>::iterator i = indexOrder.begin(); i != indexOrder.end(); i++)
262    {
263        size_t index = *i;
264        char *start = (char*)memTable[index].mtAddr;
265        char *end = start + memTable[index].mtLength;
266        for (PolyWord *p = (PolyWord*)start; p < (PolyWord*)end; )
267        {
268            p++;
269            PolyObject *obj = (PolyObject*)p;
270            POLYUNSIGNED length = obj->Length();
271            pMap.push_back(obj);
272            p += length;
273        }
274    }
275
276    /* Start writing the information. */
277    fprintf(exportFile, "Objects\t%" PRI_SIZET "\n", pMap.size());
278    fprintf(exportFile, "Root\t%" PRI_SIZET "\n", getIndex(rootFunction));
279
280    // Generate each of the areas.
281    for (size_t i = 0; i < memTableEntries; i++)
282    {
283        char *start = (char*)memTable[i].mtAddr;
284        char *end = start + memTable[i].mtLength;
285        for (PolyWord *p = (PolyWord*)start; p < (PolyWord*)end; )
286        {
287            p++;
288            PolyObject *obj = (PolyObject*)p;
289            POLYUNSIGNED length = obj->Length();
290            printObject(obj);
291            p += length;
292        }
293    }
294
295    fclose(exportFile); exportFile = NULL;
296}
297
298
299/*
300Import a portable export file and load it into memory.
301Creates "permanent" address entries in the global memory table.
302*/
303
304class SpaceAlloc
305{
306public:
307    SpaceAlloc(bool isMut, POLYUNSIGNED def);
308    ~SpaceAlloc();
309    PolyObject *NewObj(POLYUNSIGNED objWords);
310    bool AddToTable(void);
311
312    POLYUNSIGNED defaultSize;
313    POLYUNSIGNED currentSize;
314    PolyWord *base;
315    POLYUNSIGNED used;
316    bool isMutable;
317    unsigned spaceIndex;
318};
319
320SpaceAlloc::SpaceAlloc(bool isMut, POLYUNSIGNED def)
321{
322    isMutable = isMut;
323    defaultSize = def;
324    base = 0;
325    currentSize = 0;
326    used = 0;
327    spaceIndex = 1;
328}
329
330SpaceAlloc::~SpaceAlloc()
331{
332    if (base)
333        osMemoryManager->Free(base, currentSize*sizeof(PolyWord));
334}
335
336bool SpaceAlloc::AddToTable(void)
337{
338    if (base != 0)
339    {
340        // Add the new space to the permanent memory table.
341        MemSpace* space = gMem.NewPermanentSpace(base, used, isMutable ? MTF_WRITEABLE : 0, spaceIndex++);
342        if (space == 0)
343        {
344            fprintf(stderr, "Insufficient memory\n");
345            return false;
346        }
347    }
348    base = 0;
349    return true;
350}
351
352// Allocate a new object.  May create a new space and add the old one to the permanent
353// memory table if this is exhausted.
354PolyObject *SpaceAlloc::NewObj(POLYUNSIGNED objWords)
355{
356    if (currentSize - used <= objWords)
357    {
358        // Need some more space.
359        if (! AddToTable())
360            return 0;
361        POLYUNSIGNED size = defaultSize;
362        if (size <= objWords)
363            size = objWords+1;
364        size_t iSpace = size*sizeof(PolyWord);
365        base = (PolyWord*)osMemoryManager->Allocate(iSpace, PERMISSION_READ|PERMISSION_WRITE|PERMISSION_EXEC);
366        if (base == 0)
367        {
368            fprintf(stderr, "Unable to allocate memory\n");
369            return 0;
370        }
371        currentSize = iSpace/sizeof(PolyWord);
372        used = 0;
373    }
374    ASSERT(currentSize - used > objWords);
375    PolyObject *newObj = (PolyObject*)(base+used+1);
376    used += objWords+1;
377    return newObj;
378}
379
380class PImport
381{
382public:
383    PImport();
384    ~PImport();
385    bool DoImport(void);
386    FILE *f;
387    PolyObject *Root(void) { return objMap[nRoot]; }
388private:
389    PolyObject *NewObject(POLYUNSIGNED words, bool isMutable);
390    bool ReadValue(PolyObject *p, POLYUNSIGNED i);
391    bool GetValue(PolyWord *result);
392
393    POLYUNSIGNED nObjects, nRoot;
394    PolyObject **objMap;
395
396    SpaceAlloc mutSpace, immutSpace;
397};
398
399PImport::PImport(): mutSpace(true, 1024*1024), immutSpace(false, 1024*1024)
400{
401    f = NULL;
402    objMap = 0;
403}
404
405PImport::~PImport()
406{
407    if (f)
408        fclose(f);
409    free(objMap);
410}
411
412PolyObject *PImport::NewObject(POLYUNSIGNED words, bool isMutableObj)
413{
414    PolyObject *newObj = 0;
415    if (isMutableObj)
416        newObj = mutSpace.NewObj(words);
417    else
418        newObj = immutSpace.NewObj(words);
419    if (newObj == 0)
420        return 0;
421
422    return newObj;
423
424}
425
426bool PImport::GetValue(PolyWord *result)
427{
428    int ch = getc(f);
429    if (ch == '@')
430    {
431        /* Address of an object. */
432        POLYUNSIGNED obj;
433        fscanf(f, "%" POLYUFMT, &obj);
434        ASSERT(obj < nObjects);
435        *result = objMap[obj];
436    }
437    else if (ch == '$')
438    {
439        /* Code address. */
440        POLYUNSIGNED obj, offset;
441        fscanf(f, "%" POLYUFMT "+%" POLYUFMT, &obj, &offset);
442        ASSERT(obj < nObjects);
443        PolyObject *q = objMap[obj];
444        ASSERT(q->IsCodeObject());
445        *result = PolyWord::FromCodePtr((PolyWord(q)).AsCodePtr() + offset); /* The offset is in bytes. */
446    }
447    else if ((ch >= '0' && ch <= '9') || ch == '-')
448    {
449        /* Tagged integer. */
450        POLYSIGNED j;
451        ungetc(ch, f);
452        fscanf(f, "%" POLYSFMT, &j);
453        /* The assertion may be false if we are porting to a machine
454           with a shorter tagged representation. */
455        ASSERT(j >= -MAXTAGGED-1 && j <= MAXTAGGED);
456        *result = TAGGED(j);
457    }
458    else if (ch == 'I')
459    {
460        /* IO entry number. */
461        POLYUNSIGNED j;
462        fscanf(f, "%" POLYUFMT, &j);
463        // We may still have references to the old empty string value (j == 48).
464        if (j == 48)
465        {
466            // This is a bit of a hack but it's only temporary.
467            PolyObject  *p = NewObject(1, false);
468            p->SetLengthWord(1, F_BYTE_OBJ);
469            p->Set(0, PolyWord::FromUnsigned(0));
470            *result = p;
471        }
472        else ASSERT(0);
473    }
474    else
475    {
476        fprintf(stderr, "Unexpected character in stream");
477        return false;
478    }
479    return true;
480}
481
482/* Read a value and store it at the specified word. */
483bool PImport::ReadValue(PolyObject *p, POLYUNSIGNED i)
484{
485    PolyWord result = TAGGED(0);
486    if (GetValue(&result))
487    {
488        p->Set(i, result);
489        return true;
490    }
491    else return false;
492}
493
494bool PImport::DoImport()
495{
496    int ch;
497    POLYUNSIGNED objNo;
498
499    ASSERT(gMem.pSpaces.size() == 0);
500    ASSERT(gMem.eSpaces.size() == 0);
501
502    ch = getc(f);
503    /* Skip the "Mapping" line. */
504    if (ch == 'M') { while (getc(f) != '\n') ; ch = getc(f); }
505    ASSERT(ch == 'O'); /* Number of objects. */
506    while (getc(f) != '\t') ;
507    fscanf(f, "%" POLYUFMT, &nObjects);
508    /* Create a mapping table. */
509    objMap = (PolyObject**)calloc(nObjects, sizeof(PolyObject*));
510    if (objMap == 0)
511    {
512        fprintf(stderr, "Unable to allocate memory\n");
513        return false;
514    }
515
516    do
517    {
518        ch = getc(f);
519    } while (ch == '\n');
520    ASSERT(ch == 'R'); /* Root object number. */
521    while (getc(f) != '\t') ;
522    fscanf(f, "%" POLYUFMT, &nRoot);
523
524    /* Now the objects themselves. */
525    while (1)
526    {
527        bool     isMutable = false;
528        unsigned    objBits = 0;
529        POLYUNSIGNED  nWords, nBytes;
530        do
531        {
532            ch = getc(f);
533        } while (ch == '\r' || ch == '\n');
534        if (ch == EOF) break;
535        ungetc(ch, f);
536        fscanf(f, "%" POLYUFMT, &objNo);
537        ch = getc(f);
538        ASSERT(ch == ':');
539        ASSERT(objNo < nObjects);
540
541        /* Modifiers, MNVW. */
542        do
543        {
544            ch = getc(f);
545            if (ch == 'M') { isMutable = true; objBits |= F_MUTABLE_BIT; }
546            else if (ch == 'N') objBits |= F_NEGATIVE_BIT;
547            if (ch == 'V') objBits |= F_NO_OVERWRITE;
548            if (ch == 'W') objBits |= F_WEAK_BIT;
549        } while (ch == 'M' || ch == 'N' || ch == 'L' || ch == 'V' || ch == 'W');
550
551        /* Object type. */
552        switch (ch)
553        {
554        case 'O': /* Simple object. */
555            fscanf(f, "%" POLYUFMT, &nWords);
556            break;
557
558        case 'B': /* Byte segment. */
559            objBits |= F_BYTE_OBJ;
560            fscanf(f, "%" POLYUFMT, &nBytes);
561            /* Round up to appropriate number of words. */
562            nWords = (nBytes + sizeof(PolyWord) -1) / sizeof(PolyWord);
563            break;
564
565        case 'S': /* String. */
566            objBits |= F_BYTE_OBJ;
567            /* The length is the number of characters. */
568            fscanf(f, "%" POLYUFMT, &nBytes);
569            /* Round up to appropriate number of words.  Need to add
570               one PolyWord for the length PolyWord.  */
571            nWords = (nBytes + sizeof(PolyWord) -1) / sizeof(PolyWord) + 1;
572            break;
573
574        case 'C': /* Code segment (old form). */
575        case 'D': /* Code segment (new form). */
576            objBits |= F_CODE_OBJ;
577            /* Read the number of bytes of code and the number of words
578               for constants. */
579            fscanf(f, "%" POLYUFMT ",%" POLYUFMT, &nWords, &nBytes);
580            nWords += ch == 'C' ? 4 : 1; /* Add words for extras. */
581            /* Add in the size of the code itself. */
582            nWords += (nBytes + sizeof(PolyWord) -1) / sizeof(PolyWord);
583            break;
584
585        default:
586            fprintf(stderr, "Invalid object type\n");
587            return false;
588        }
589
590        PolyObject  *p = NewObject(nWords, isMutable);
591        if (p == 0)
592            return false;
593        objMap[objNo] = p;
594        /* Put in length PolyWord and flag bits. */
595        p->SetLengthWord(nWords, objBits);
596
597        /* Skip the object contents. */
598        while (getc(f) != '\n') ;
599    }
600
601    /* Second pass - fill in the contents. */
602    fseek(f, 0, SEEK_SET);
603    /* Skip the information at the start. */
604    ch = getc(f);
605    if (ch == 'M')
606    {
607        while (getc(f) != '\n') ;
608        ch = getc(f);
609    }
610    ASSERT(ch == 'O'); /* Number of objects. */
611    while (getc(f) != '\n');
612    ch = getc(f);
613    ASSERT(ch == 'R'); /* Root object number. */
614    while (getc(f) != '\n') ;
615
616    while (1)
617    {
618        POLYUNSIGNED  nWords, nBytes, i;
619        if (feof(f))
620            break;
621        fscanf(f, "%" POLYUFMT, &objNo);
622        if (feof(f))
623            break;
624        ch = getc(f);
625        ASSERT(ch == ':');
626        ASSERT(objNo < nObjects);
627        PolyObject * p = objMap[objNo];
628
629        /* Modifiers, M or N. */
630        do
631        {
632            ch = getc(f);
633        } while (ch == 'M' || ch == 'N' || ch == 'L' || ch == 'V' || ch == 'W');
634
635        /* Object type. */
636        switch (ch)
637        {
638        case 'O': /* Simple object. */
639            fscanf(f, "%" POLYUFMT, &nWords);
640            ch = getc(f);
641            ASSERT(ch == '|');
642            ASSERT(nWords == p->Length());
643
644            for (i = 0; i < nWords; i++)
645            {
646                if (! ReadValue(p, i))
647                    return false;
648                ch = getc(f);
649                ASSERT((ch == ',' && i < nWords-1) ||
650                       (ch == '\n' && i == nWords-1));
651            }
652
653            break;
654
655        case 'B': /* Byte segment. */
656            {
657                byte *u = (byte*)p;
658                fscanf(f, "%" POLYUFMT, &nBytes);
659                ch = getc(f); ASSERT(ch == '|');
660                for (i = 0; i < nBytes; i++)
661                {
662                    int n;
663                    fscanf(f, "%02x", &n);
664                    u[i] = n;
665                }
666                ch = getc(f);
667                ASSERT(ch == '\n');
668                // If this is an entry point object set its value.
669                if (p->IsMutable() && p->IsWeakRefObject())
670                {
671                    bool loadEntryPt = setEntryPoint(p);
672                    ASSERT(loadEntryPt);
673                }
674                break;
675            }
676
677        case 'S': /* String. */
678            {
679                PolyStringObject * ps = (PolyStringObject *)p;
680                /* The length is the number of characters. */
681                fscanf(f, "%" POLYUFMT, &nBytes);
682                ch = getc(f); ASSERT(ch == '|');
683                ps->length = nBytes;
684                for (i = 0; i < nBytes; i++)
685                {
686                    int n;
687                    fscanf(f, "%02x", &n);
688                    ps->chars[i] = n;
689                }
690                ch = getc(f);
691                ASSERT(ch == '\n');
692                break;
693            }
694
695        case 'C': /* Code segment. */
696        case 'D':
697            {
698                bool oldForm = ch == 'C';
699                byte *u = (byte*)p;
700                POLYUNSIGNED length = p->Length();
701                /* Read the number of bytes of code and the number of words
702                   for constants. */
703                fscanf(f, "%" POLYUFMT ",%" POLYUFMT, &nWords, &nBytes);
704                /* Read the code. */
705                ch = getc(f); ASSERT(ch == '|');
706                for (i = 0; i < nBytes; i++)
707                {
708                    int n;
709                    fscanf(f, "%02x", &n);
710                    u[i] = n;
711                }
712                machineDependent->FlushInstructionCache(u, nBytes);
713                ch = getc(f);
714                ASSERT(ch == '|');
715                /* Set the constant count. */
716                p->Set(length-1, PolyWord::FromUnsigned(nWords));
717                if (oldForm)
718                {
719                    p->Set(length-1-nWords-1, PolyWord::FromUnsigned(0)); /* Profile count. */
720                    p->Set(length-1-nWords-3, PolyWord::FromUnsigned(0)); /* Marker word. */
721                    p->Set(length-1-nWords-2, PolyWord::FromUnsigned((length-1-nWords-2)*sizeof(PolyWord)));
722                    /* Check - the code should end at the marker word. */
723                    ASSERT(nBytes == ((length-1-nWords-3)*sizeof(PolyWord)));
724                }
725                /* Read in the constants. */
726                for (i = 0; i < nWords; i++)
727                {
728                    if (! ReadValue(p, i+length-nWords-1))
729                        return false;
730                    ch = getc(f);
731                    ASSERT((ch == ',' && i < nWords-1) ||
732                           ((ch == '\n' || ch == '|') && i == nWords-1));
733                }
734                // Read in any constants in the code.
735                if (ch == '|')
736                {
737                    ch = getc(f);
738                    while (ch != '\n')
739                    {
740                        ungetc(ch, f);
741                        POLYUNSIGNED offset;
742                        int code;
743                        fscanf(f, "%" POLYUFMT ",%d", &offset, &code);
744                        ch = getc(f);
745                        ASSERT(ch == ',');
746                        PolyWord constVal = TAGGED(0);
747                        if (! GetValue(&constVal))
748                            return false;
749                        byte *toPatch = (byte*)p + offset;
750                        ScanAddress::SetConstantValue(toPatch, constVal, (ScanRelocationKind)code);
751
752                        do ch = getc(f); while (ch == ' ');
753                    }
754                }
755                break;
756            }
757
758        default:
759            fprintf(stderr, "Invalid object type\n");
760            return false;
761        }
762    }
763    return mutSpace.AddToTable() && immutSpace.AddToTable();
764}
765
766// Import a file in the portable format and return a pointer to the root object.
767PolyObject *ImportPortable(const TCHAR *fileName)
768{
769    PImport pImport;
770#if (defined(_WIN32) && defined(UNICODE))
771    pImport.f = _wfopen(fileName, L"r");
772    if (pImport.f == 0)
773    {
774        fprintf(stderr, "Unable to open file: %S\n", fileName);
775        return 0;
776    }
777#else
778    pImport.f = fopen(fileName, "r");
779    if (pImport.f == 0)
780    {
781        fprintf(stderr, "Unable to open file: %s\n", fileName);
782        return 0;
783    }
784#endif
785    if (pImport.DoImport())
786        return pImport.Root();
787    else
788        return 0;
789}
790