1/*
2    Title:  savestate.cpp - Save and Load state
3
4    Copyright (c) 2007, 2015 David C.J. Matthews
5
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License version 2.1 as published by the Free Software Foundation.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19*/
20
21
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#elif defined(_WIN32)
25#include "winconfig.h"
26#else
27#error "No configuration file"
28#endif
29
30#ifdef HAVE_STDIO_H
31#include <stdio.h>
32#endif
33
34#ifdef HAVE_WINDOWS_H
35#include <windows.h> // For MAX_PATH
36#endif
37
38#ifdef HAVE_SYS_PARAM_H
39#include <sys/param.h> // For MAX_PATH
40#endif
41
42#ifdef HAVE_ERRNO_H
43#include <errno.h>
44#endif
45
46#ifdef HAVE_TIME_H
47#include <time.h>
48#endif
49
50#ifdef HAVE_SYS_TYPES_H
51#include <sys/types.h>
52#endif
53
54#ifdef HAVE_SYS_STAT_H
55#include <sys/stat.h>
56#endif
57
58#ifdef HAVE_UNISTD_H
59#include <unistd.h>
60#endif
61
62#ifdef HAVE_STRING_H
63#include <string.h>
64#endif
65
66#ifdef HAVE_ASSERT_H
67#include <assert.h>
68#define ASSERT(x)   assert(x)
69#else
70#define ASSERT(x)
71#endif
72
73#if (defined(_WIN32) && ! defined(__CYGWIN__))
74#include <tchar.h>
75#define ERRORNUMBER _doserrno
76#define NOMEMORY ERROR_NOT_ENOUGH_MEMORY
77#else
78typedef char TCHAR;
79#define _T(x) x
80#define _tfopen fopen
81#define _tcscpy strcpy
82#define _tcsdup strdup
83#define _tcslen strlen
84#define _fputtc fputc
85#define _fputts fputs
86#ifndef lstrcmpi
87#define lstrcmpi strcasecmp
88#endif
89#define ERRORNUMBER errno
90#define NOMEMORY ENOMEM
91#endif
92
93
94#include "globals.h"
95#include "savestate.h"
96#include "processes.h"
97#include "run_time.h"
98#include "polystring.h"
99#include "scanaddrs.h"
100#include "arb.h"
101#include "memmgr.h"
102#include "mpoly.h" // For exportTimeStamp
103#include "exporter.h" // For CopyScan
104#include "machine_dep.h"
105#include "osmem.h"
106#include "gc.h" // For FullGC.
107#include "timing.h"
108#include "rtsentry.h"
109#include "check_objects.h"
110
111#ifdef _MSC_VER
112// Don't tell me about ISO C++ changes.
113#pragma warning(disable:4996)
114#endif
115
116// Helper class to close files on exit.
117class AutoClose {
118public:
119    AutoClose(FILE *f = 0): m_file(f) {}
120    ~AutoClose() { if (m_file) ::fclose(m_file); }
121
122    operator FILE*() { return m_file; }
123    FILE* operator = (FILE* p)  { return (m_file = p); }
124
125private:
126    FILE *m_file;
127};
128
129// This is probably generally useful so may be moved into
130// a general header file.
131template<typename BASE> class AutoFree
132{
133public:
134    AutoFree(BASE p = 0): m_value(p) {}
135    ~AutoFree() { free(m_value); }
136
137    // Automatic conversions to the base type.
138    operator BASE() { return m_value; }
139    BASE operator = (BASE p)  { return (m_value = p); }
140
141private:
142    BASE m_value;
143};
144
145/*
146 *  Structure definitions for the saved state files.
147 */
148
149#define SAVEDSTATESIGNATURE "POLYSAVE"
150#define SAVEDSTATEVERSION   2
151
152// File header for a saved state file.  This appears as the first entry
153// in the file.
154typedef struct _savedStateHeader
155{
156    // These entries are primarily to check that we have a valid
157    // saved state file before we try to interpret anything else.
158    char        headerSignature[8];     // Should contain SAVEDSTATESIGNATURE
159    unsigned    headerVersion;          // Should contain SAVEDSTATEVERSION
160    unsigned    headerLength;           // Number of bytes in the header
161    unsigned    segmentDescrLength;     // Number of bytes in a descriptor
162
163    // These entries contain the real data.
164    off_t       segmentDescr;           // Position of segment descriptor table
165    unsigned    segmentDescrCount;      // Number of segment descriptors in the table
166    off_t       stringTable;            // Pointer to the string table (zero if none)
167    size_t      stringTableSize;        // Size of string table
168    unsigned    parentNameEntry;        // Position of parent name in string table (0 if top)
169    time_t      timeStamp;            // The time stamp for this file.
170    time_t      parentTimeStamp;      // The time stamp for the parent.
171} SavedStateHeader;
172
173// Entry for segment table.  This describes the segments on the disc that
174// need to be loaded into memory.
175typedef struct _savedStateSegmentDescr
176{
177    off_t       segmentData;            // Position of the segment data
178    size_t      segmentSize;            // Size of the segment data
179    off_t       relocations;            // Position of the relocation table
180    unsigned    relocationCount;        // Number of entries in relocation table
181    unsigned    relocationSize;         // Size of a relocation entry
182    unsigned    segmentFlags;           // Segment flags (see SSF_ values)
183    unsigned    segmentIndex;           // The index of this segment or the segment it overwrites
184    void        *originalAddress;       // The base address when the segment was written.
185} SavedStateSegmentDescr;
186
187#define SSF_WRITABLE    1               // The segment contains mutable data
188#define SSF_OVERWRITE   2               // The segment overwrites the data (mutable) in a parent.
189#define SSF_NOOVERWRITE 4               // The segment must not be further overwritten
190#define SSF_BYTES       8               // The segment contains only byte data
191#define SSF_CODE        16              // The segment contains only code
192
193typedef struct _relocationEntry
194{
195    // Each entry indicates a location that has to be set to an address.
196    // The location to be set is determined by adding "relocAddress" to the base address of
197    // this segment (the one to which these relocations apply) and the value to store
198    // by adding "targetAddress" to the base address of the segment indicated by "targetSegment".
199    POLYUNSIGNED    relocAddress;       // The (byte) offset in this segment that we will set
200    POLYUNSIGNED    targetAddress;      // The value to add to the base of the destination segment
201    unsigned        targetSegment;      // The base segment.  0 is IO segment.
202    ScanRelocationKind relKind;         // The kind of relocation (processor dependent).
203} RelocationEntry;
204
205#define SAVE(x) taskData->saveVec.push(x)
206
207/*
208 *  Hierarchy table: contains information about last loaded or saved state.
209 */
210
211// Pointer to list of files loaded in last load.
212// There's no need for a lock since the update is only made when all
213// the ML threads have stopped.
214class HierarchyTable
215{
216public:
217    HierarchyTable(const TCHAR *file, time_t time):
218      fileName(_tcsdup(file)), timeStamp(time) { }
219    AutoFree<TCHAR*> fileName;
220    time_t          timeStamp;
221};
222
223HierarchyTable **hierarchyTable;
224
225static unsigned hierarchyDepth;
226
227static bool AddHierarchyEntry(const TCHAR *fileName, time_t timeStamp)
228{
229    // Add an entry to the hierarchy table for this file.
230    HierarchyTable *newEntry = new HierarchyTable(fileName, timeStamp);
231    if (newEntry == 0) return false;
232    HierarchyTable **newTable =
233        (HierarchyTable **)realloc(hierarchyTable, sizeof(HierarchyTable *)*(hierarchyDepth+1));
234    if (newTable == 0) return false;
235    hierarchyTable = newTable;
236    hierarchyTable[hierarchyDepth++] = newEntry;
237    return true;
238}
239
240// Test whether we're overwriting a parent of ourself.
241#if (defined(_WIN32) || defined(__CYGWIN__))
242static bool sameFile(const TCHAR *x, const TCHAR *y)
243{
244    HANDLE hXFile = INVALID_HANDLE_VALUE, hYFile = INVALID_HANDLE_VALUE;
245    bool result = false;
246
247    hXFile = CreateFile(x, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
248    if (hXFile == INVALID_HANDLE_VALUE) goto closeAndExit;
249    hYFile = CreateFile(y, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
250    if (hYFile == INVALID_HANDLE_VALUE) goto closeAndExit;
251    BY_HANDLE_FILE_INFORMATION fileInfoX, fileInfoY;
252    if (! GetFileInformationByHandle(hXFile, &fileInfoX)) goto closeAndExit;
253    if (! GetFileInformationByHandle(hYFile, &fileInfoY)) goto closeAndExit;
254
255    result = fileInfoX.dwVolumeSerialNumber == fileInfoY.dwVolumeSerialNumber &&
256        fileInfoX.nFileIndexLow == fileInfoY.nFileIndexLow &&
257        fileInfoX.nFileIndexHigh == fileInfoY.nFileIndexHigh;
258
259closeAndExit:
260    if (hXFile != INVALID_HANDLE_VALUE) CloseHandle(hXFile);
261    if (hYFile != INVALID_HANDLE_VALUE) CloseHandle(hYFile);
262    return result;
263}
264#else
265static bool sameFile(const char *x, const char *y)
266{
267    struct stat xStat, yStat;
268    // If either file does not exist that's fine.
269    if (stat(x, &xStat) != 0 || stat(y, &yStat) != 0)
270        return false;
271    return (xStat.st_dev == yStat.st_dev && xStat.st_ino == yStat.st_ino);
272}
273#endif
274
275/*
276 *  Saving state.
277 */
278
279// This class is used to create the relocations.  It uses Exporter
280// for this but this may perhaps be too heavyweight.
281class SaveStateExport: public Exporter, public ScanAddress
282{
283public:
284    SaveStateExport(unsigned int h=0): Exporter(h), relocationCount(0) {}
285public:
286    virtual void exportStore(void) {} // Not used.
287
288private:
289    // ScanAddress overrides
290    virtual void ScanConstant(PolyObject *base, byte *addrOfConst, ScanRelocationKind code);
291    // At the moment we should only get calls to ScanConstant.
292    virtual PolyObject *ScanObjectAddress(PolyObject *base) { return base; }
293
294protected:
295    void setRelocationAddress(void *p, POLYUNSIGNED *reloc);
296    PolyWord createRelocation(PolyWord p, void *relocAddr);
297    unsigned relocationCount;
298
299    friend class SaveRequest;
300};
301
302// Generate the address relative to the start of the segment.
303void SaveStateExport::setRelocationAddress(void *p, POLYUNSIGNED *reloc)
304{
305    unsigned area = findArea(p);
306    POLYUNSIGNED offset = (char*)p - (char*)memTable[area].mtAddr;
307    *reloc = offset;
308}
309
310
311// Create a relocation entry for an address at a given location.
312PolyWord SaveStateExport::createRelocation(PolyWord p, void *relocAddr)
313{
314    RelocationEntry reloc;
315    // Set the offset within the section we're scanning.
316    setRelocationAddress(relocAddr, &reloc.relocAddress);
317    void *addr = p.AsAddress();
318    unsigned addrArea = findArea(addr);
319    reloc.targetAddress = (char*)addr - (char*)memTable[addrArea].mtAddr;
320    reloc.targetSegment = (unsigned)memTable[addrArea].mtIndex;
321    reloc.relKind = PROCESS_RELOC_DIRECT;
322    fwrite(&reloc, sizeof(reloc), 1, exportFile);
323    relocationCount++;
324    return p; // Don't change the contents
325}
326
327
328/* This is called for each constant within the code.
329   Print a relocation entry for the word and return a value that means
330   that the offset is saved in original word. */
331void SaveStateExport::ScanConstant(PolyObject *base, byte *addr, ScanRelocationKind code)
332{
333    PolyWord p = GetConstantValue(addr, code);
334
335    if (IS_INT(p) || p == PolyWord::FromUnsigned(0))
336        return;
337
338    void *a = p.AsAddress();
339    unsigned aArea = findArea(a);
340
341    // We don't need a relocation if this is relative to the current segment
342    // since the relative address will already be right.
343    if (code == PROCESS_RELOC_I386RELATIVE && aArea == findArea(addr))
344        return;
345
346    // Set the value at the address to the offset relative to the symbol.
347    RelocationEntry reloc;
348    setRelocationAddress(addr, &reloc.relocAddress);
349    reloc.targetAddress = (char*)a - (char*)memTable[aArea].mtAddr;
350    reloc.targetSegment = (unsigned)memTable[aArea].mtIndex;
351    reloc.relKind = code;
352    fwrite(&reloc, sizeof(reloc), 1, exportFile);
353    relocationCount++;
354}
355
356// Request to the main thread to save data.
357class SaveRequest: public MainThreadRequest
358{
359public:
360    SaveRequest(const TCHAR *name, unsigned h): MainThreadRequest(MTP_SAVESTATE),
361        fileName(name), newHierarchy(h),
362        errorMessage(0), errCode(0) {}
363
364    virtual void Perform();
365    const TCHAR *fileName;
366    unsigned newHierarchy;
367    const char *errorMessage;
368    int errCode;
369};
370
371// This class is used to update references to objects that have moved.  If
372// we have copied an object into the area to be exported we may still have references
373// to it from the stack or from RTS data structures.  We have to ensure that these
374// are updated.
375// This is very similar to ProcessFixupAddress in sharedata.cpp
376class SaveFixupAddress: public ScanAddress
377{
378protected:
379    virtual POLYUNSIGNED ScanAddressAt(PolyWord *pt);
380    virtual PolyObject *ScanObjectAddress(PolyObject *base)
381        { return GetNewAddress(base).AsObjPtr(); }
382    PolyWord GetNewAddress(PolyWord old);
383
384public:
385    void ScanCodeSpace(CodeSpace *space);
386};
387
388
389POLYUNSIGNED SaveFixupAddress::ScanAddressAt(PolyWord *pt)
390{
391    *pt = GetNewAddress(*pt);
392    return 0;
393}
394
395// Returns the new address if the argument is the address of an object that
396// has moved, otherwise returns the original.
397PolyWord SaveFixupAddress::GetNewAddress(PolyWord old)
398{
399    if (old.IsTagged() || old == PolyWord::FromUnsigned(0))
400        return old; //  Nothing to do.
401
402    ASSERT(old.IsDataPtr());
403
404    PolyObject *obj = old.AsObjPtr();
405
406    if (obj->ContainsForwardingPtr()) // tombstone is a pointer to a moved object
407    {
408        PolyObject *newp = obj->GetForwardingPtr();
409        ASSERT (newp->ContainsNormalLengthWord());
410        return newp;
411    }
412
413    ASSERT (obj->ContainsNormalLengthWord()); // object is not moved
414    return old;
415}
416
417// Fix up addresses in the code area.  Unlike ScanAddressesInRegion this updates
418// cells that have been moved.  We need to do that because we may still have
419// return addresses into those cells and we don't move return addresses.  We
420// do want the code to see updated constant addresses.
421void SaveFixupAddress::ScanCodeSpace(CodeSpace *space)
422{
423    for (PolyWord *pt = space->bottom; pt < space->top; )
424    {
425        pt++;
426        PolyObject *obj = (PolyObject*)pt;
427        PolyObject *dest = obj->FollowForwardingChain();
428        POLYUNSIGNED length = dest->Length();
429        if (length != 0)
430            ScanAddressesInObject(obj, dest->LengthWord());
431        pt += length;
432    }
433}
434
435// Called by the root thread to actually save the state and write the file.
436void SaveRequest::Perform()
437{
438    if (debugOptions & DEBUG_SAVING)
439        Log("SAVE: Beginning saving state.\n");
440    // Check that we aren't overwriting our own parent.
441    for (unsigned q = 0; q < newHierarchy-1; q++) {
442        if (sameFile(hierarchyTable[q]->fileName, fileName))
443        {
444            errorMessage = "File being saved is used as a parent of this file";
445            errCode = 0;
446            if (debugOptions & DEBUG_SAVING)
447                Log("SAVE: File being saved is used as a parent of this file.\n");
448            return;
449        }
450    }
451
452    SaveStateExport exports;
453    // Open the file.  This could quite reasonably fail if the path is wrong.
454    exports.exportFile = _tfopen(fileName, _T("wb"));
455    if (exports.exportFile == NULL)
456    {
457        errorMessage = "Cannot open save file";
458        errCode = ERRORNUMBER;
459        if (debugOptions & DEBUG_SAVING)
460            Log("SAVE: Cannot open save file.\n");
461        return;
462    }
463
464    // Scan over the permanent mutable area copying all reachable data that is
465    // not in a lower hierarchy into new permanent segments.
466    CopyScan copyScan(newHierarchy);
467    copyScan.initialise(false);
468    bool success = true;
469    try {
470        for (std::vector<PermanentMemSpace*>::iterator i = gMem.pSpaces.begin(); i < gMem.pSpaces.end(); i++)
471        {
472            PermanentMemSpace *space = *i;
473            if (space->isMutable && !space->noOverwrite && !space->byteOnly)
474            {
475                if (debugOptions & DEBUG_SAVING)
476                    Log("SAVE: Scanning permanent mutable area %p allocated at %p size %lu\n",
477                        space, space->bottom, space->spaceSize());
478                copyScan.ScanAddressesInRegion(space->bottom, space->top);
479            }
480        }
481    }
482    catch (MemoryException &)
483    {
484        success = false;
485        if (debugOptions & DEBUG_SAVING)
486            Log("SAVE: Scan of permanent mutable area raised memory exception.\n");
487    }
488
489    // Copy the areas into the export object.  Make sufficient space for
490    // the largest possible number of entries.
491    exports.memTable = new memoryTableEntry[gMem.eSpaces.size()+gMem.pSpaces.size()+1];
492    unsigned memTableCount = 0;
493
494    // Permanent spaces at higher level.  These have to have entries although
495    // only the mutable entries will be written.
496    for (std::vector<PermanentMemSpace*>::iterator i = gMem.pSpaces.begin(); i < gMem.pSpaces.end(); i++)
497    {
498        PermanentMemSpace *space = *i;
499        if (space->hierarchy < newHierarchy)
500        {
501            memoryTableEntry *entry = &exports.memTable[memTableCount++];
502            entry->mtAddr = space->bottom;
503            entry->mtLength = (space->topPointer-space->bottom)*sizeof(PolyWord);
504            entry->mtIndex = space->index;
505            entry->mtFlags = 0;
506            if (space->isMutable)
507            {
508                entry->mtFlags |= MTF_WRITEABLE;
509                if (space->noOverwrite) entry->mtFlags |= MTF_NO_OVERWRITE;
510                if (space->byteOnly) entry->mtFlags |= MTF_BYTES;
511            }
512            if (space->isCode)
513                entry->mtFlags |= MTF_EXECUTABLE;
514        }
515    }
516    unsigned permanentEntries = memTableCount; // Remember where new entries start.
517
518    // Newly created spaces.
519    for (std::vector<PermanentMemSpace *>::iterator i = gMem.eSpaces.begin(); i < gMem.eSpaces.end(); i++)
520    {
521        memoryTableEntry *entry = &exports.memTable[memTableCount++];
522        PermanentMemSpace *space = *i;
523        entry->mtAddr = space->bottom;
524        entry->mtLength = (space->topPointer-space->bottom)*sizeof(PolyWord);
525        entry->mtIndex = space->index;
526        entry->mtFlags = 0;
527        if (space->isMutable)
528        {
529            entry->mtFlags |= MTF_WRITEABLE;
530            if (space->noOverwrite) entry->mtFlags |= MTF_NO_OVERWRITE;
531            if (space->byteOnly) entry->mtFlags |= MTF_BYTES;
532        }
533        if (space->isCode)
534            entry->mtFlags |= MTF_EXECUTABLE;
535    }
536
537    exports.memTableEntries = memTableCount;
538
539    if (debugOptions & DEBUG_SAVING)
540        Log("SAVE: Updating references to moved objects.\n");
541
542    // Update references to moved objects.
543    SaveFixupAddress fixup;
544    for (std::vector<LocalMemSpace*>::iterator i = gMem.lSpaces.begin(); i < gMem.lSpaces.end(); i++)
545    {
546        LocalMemSpace *space = *i;
547        fixup.ScanAddressesInRegion(space->bottom, space->lowerAllocPtr);
548        fixup.ScanAddressesInRegion(space->upperAllocPtr, space->top);
549    }
550    for (std::vector<CodeSpace *>::iterator i = gMem.cSpaces.begin(); i < gMem.cSpaces.end(); i++)
551        fixup.ScanCodeSpace(*i);
552
553    GCModules(&fixup);
554
555    // Restore the length words in the code areas.
556    // Although we've updated any pointers to the start of the code we could have return addresses
557    // pointing to the original code.  GCModules updates the stack but doesn't update return addresses.
558    for (std::vector<CodeSpace *>::iterator i = gMem.cSpaces.begin(); i < gMem.cSpaces.end(); i++)
559    {
560        CodeSpace *space = *i;
561        for (PolyWord *pt = space->bottom; pt < space->top; )
562        {
563            pt++;
564            PolyObject *obj = (PolyObject*)pt;
565            if (obj->ContainsForwardingPtr())
566            {
567                PolyObject *forwardedTo = obj->FollowForwardingChain();
568                POLYUNSIGNED lengthWord = forwardedTo->LengthWord();
569                obj->SetLengthWord(lengthWord);
570            }
571            pt += obj->Length();
572        }
573    }
574
575    // Update the global memory space table.  Old segments at the same level
576    // or lower are removed.  The new segments become permanent.
577    // Try to promote the spaces even if we've had a failure because export
578    // spaces are deleted in ~CopyScan and we may have already copied
579    // some objects there.
580    if (debugOptions & DEBUG_SAVING)
581        Log("SAVE: Promoting export spaces to permanent spaces.\n");
582    if (! gMem.PromoteExportSpaces(newHierarchy) || ! success)
583    {
584        errorMessage = "Out of Memory";
585        errCode = NOMEMORY;
586        if (debugOptions & DEBUG_SAVING)
587            Log("SAVE: Unable to promote export spaces.\n");
588        return;
589    }
590    // Remove any deeper entries from the hierarchy table.
591    while (hierarchyDepth > newHierarchy-1)
592    {
593        hierarchyDepth--;
594        delete(hierarchyTable[hierarchyDepth]);
595        hierarchyTable[hierarchyDepth] = 0;
596    }
597
598    if (debugOptions & DEBUG_SAVING)
599        Log("SAVE: Writing out data.\n");
600
601    // Write out the file header.
602    SavedStateHeader saveHeader;
603    memset(&saveHeader, 0, sizeof(saveHeader));
604    saveHeader.headerLength = sizeof(saveHeader);
605    strncpy(saveHeader.headerSignature,
606        SAVEDSTATESIGNATURE, sizeof(saveHeader.headerSignature));
607    saveHeader.headerVersion = SAVEDSTATEVERSION;
608    saveHeader.segmentDescrLength = sizeof(SavedStateSegmentDescr);
609    if (newHierarchy == 1)
610        saveHeader.parentTimeStamp = exportTimeStamp;
611    else
612    {
613        saveHeader.parentTimeStamp = hierarchyTable[newHierarchy-2]->timeStamp;
614        saveHeader.parentNameEntry = sizeof(TCHAR); // Always the first entry.
615    }
616    saveHeader.timeStamp = getBuildTime();
617    saveHeader.segmentDescrCount = exports.memTableEntries; // One segment for each space.
618    // Write out the header.
619    fwrite(&saveHeader, sizeof(saveHeader), 1, exports.exportFile);
620
621    // We need a segment header for each permanent area whether it is
622    // actually in this file or not.
623    SavedStateSegmentDescr *descrs = new SavedStateSegmentDescr [exports.memTableEntries];
624
625    for (unsigned j = 0; j < exports.memTableEntries; j++)
626    {
627        memoryTableEntry *entry = &exports.memTable[j];
628        memset(&descrs[j], 0, sizeof(SavedStateSegmentDescr));
629        descrs[j].relocationSize = sizeof(RelocationEntry);
630        descrs[j].segmentIndex = (unsigned)entry->mtIndex;
631        descrs[j].segmentSize = entry->mtLength; // Set this even if we don't write it.
632        descrs[j].originalAddress = entry->mtAddr;
633        if (entry->mtFlags & MTF_WRITEABLE)
634        {
635            descrs[j].segmentFlags |= SSF_WRITABLE;
636            if (entry->mtFlags & MTF_NO_OVERWRITE)
637                descrs[j].segmentFlags |= SSF_NOOVERWRITE;
638            if (j < permanentEntries && (entry->mtFlags & MTF_NO_OVERWRITE) == 0)
639                descrs[j].segmentFlags |= SSF_OVERWRITE;
640            if (entry->mtFlags & MTF_BYTES)
641                descrs[j].segmentFlags |= SSF_BYTES;
642        }
643        if (entry->mtFlags & MTF_EXECUTABLE)
644            descrs[j].segmentFlags |= SSF_CODE;
645    }
646    // Write out temporarily. Will be overwritten at the end.
647    saveHeader.segmentDescr = ftell(exports.exportFile);
648    fwrite(descrs, sizeof(SavedStateSegmentDescr), exports.memTableEntries, exports.exportFile);
649
650    // Write out the relocations and the data.
651    for (unsigned k = 1 /* Not IO area */; k < exports.memTableEntries; k++)
652    {
653        memoryTableEntry *entry = &exports.memTable[k];
654        // Write out the contents if this is new or if it is a normal, overwritable
655        // mutable area.
656        if (k >= permanentEntries ||
657            (entry->mtFlags & (MTF_WRITEABLE|MTF_NO_OVERWRITE)) == MTF_WRITEABLE)
658        {
659            descrs[k].relocations = ftell(exports.exportFile);
660            // Have to write this out.
661            exports.relocationCount = 0;
662            // Create the relocation table.
663            char *start = (char*)entry->mtAddr;
664            char *end = start + entry->mtLength;
665            for (PolyWord *p = (PolyWord*)start; p < (PolyWord*)end; )
666            {
667                p++;
668                PolyObject *obj = (PolyObject*)p;
669                POLYUNSIGNED length = obj->Length();
670                // Most relocations can be computed when the saved state is
671                // loaded so we only write out the difficult ones: those that
672                // occur within compiled code.
673                //  exports.relocateObject(obj);
674                if (length != 0 && obj->IsCodeObject())
675                    machineDependent->ScanConstantsWithinCode(obj, &exports);
676                p += length;
677            }
678            descrs[k].relocationCount = exports.relocationCount;
679            // Write out the data.
680            descrs[k].segmentData = ftell(exports.exportFile);
681            fwrite(entry->mtAddr, entry->mtLength, 1, exports.exportFile);
682       }
683    }
684
685    // If this is a child we need to write a string table containing the parent name.
686    if (newHierarchy > 1)
687    {
688        saveHeader.stringTable = ftell(exports.exportFile);
689        _fputtc(0, exports.exportFile); // First byte of string table is zero
690        _fputts(hierarchyTable[newHierarchy-2]->fileName, exports.exportFile);
691        _fputtc(0, exports.exportFile); // A terminating null.
692        saveHeader.stringTableSize = (_tcslen(hierarchyTable[newHierarchy-2]->fileName) + 2)*sizeof(TCHAR);
693    }
694
695    // Rewrite the header and the segment tables now they're complete.
696    fseek(exports.exportFile, 0, SEEK_SET);
697    fwrite(&saveHeader, sizeof(saveHeader), 1, exports.exportFile);
698    fwrite(descrs, sizeof(SavedStateSegmentDescr), exports.memTableEntries, exports.exportFile);
699
700    if (debugOptions & DEBUG_SAVING)
701        Log("SAVE: Writing complete.\n");
702
703    // Add an entry to the hierarchy table for this file.
704    (void)AddHierarchyEntry(fileName, saveHeader.timeStamp);
705
706    delete[](descrs);
707
708    CheckMemory();
709}
710
711Handle SaveState(TaskData *taskData, Handle args)
712{
713    TempString fileNameBuff(Poly_string_to_T_alloc(DEREFHANDLE(args)->Get(0)));
714    // The value of depth is zero for top-level save so we need to add one for hierarchy.
715    unsigned newHierarchy = get_C_unsigned(taskData, DEREFHANDLE(args)->Get(1)) + 1;
716
717    if (newHierarchy > hierarchyDepth+1)
718        raise_fail(taskData, "Depth must be no more than the current hierarchy plus one");
719
720    // Request a full GC first.  The main reason is to avoid running out of memory as a
721    // result of repeated saves.  Old export spaces are turned into local spaces and
722    // the GC will delete them if they are completely empty
723    FullGC(taskData);
724
725    SaveRequest request(fileNameBuff, newHierarchy);
726    processes->MakeRootRequest(taskData, &request);
727    if (request.errorMessage)
728        raise_syscall(taskData, request.errorMessage, request.errCode);
729    return SAVE(TAGGED(0));
730}
731
732/*
733 *  Loading saved state files.
734 */
735
736class StateLoader: public MainThreadRequest
737{
738public:
739    StateLoader(bool isH, Handle files): MainThreadRequest(MTP_LOADSTATE),
740        isHierarchy(isH), fileNameList(files), errorResult(0), errNumber(0) { }
741
742    virtual void Perform(void);
743    bool LoadFile(bool isInitial, time_t requiredStamp, PolyWord tail);
744    bool isHierarchy;
745    Handle fileNameList;
746    const char *errorResult;
747    // The fileName here is the last file loaded.  As well as using it
748    // to load the name can also be printed out at the end to identify the
749    // particular file in the hierarchy that failed.
750    AutoFree<TCHAR*> fileName;
751    int errNumber;
752};
753
754// Called by the main thread once all the ML threads have stopped.
755void StateLoader::Perform(void)
756{
757    // Copy the first file name into the buffer.
758    if (isHierarchy)
759    {
760        if (ML_Cons_Cell::IsNull(fileNameList->Word()))
761        {
762            errorResult = "Hierarchy list is empty";
763            return;
764        }
765        ML_Cons_Cell *p = DEREFLISTHANDLE(fileNameList);
766        fileName = Poly_string_to_T_alloc(p->h);
767        if (fileName == NULL)
768        {
769            errorResult = "Insufficient memory";
770            errNumber = NOMEMORY;
771            return;
772        }
773        (void)LoadFile(true, 0, p->t);
774    }
775    else
776    {
777        fileName = Poly_string_to_T_alloc(fileNameList->Word());
778        if (fileName == NULL)
779        {
780            errorResult = "Insufficient memory";
781            errNumber = NOMEMORY;
782            return;
783        }
784        (void)LoadFile(true, 0, TAGGED(0));
785    }
786}
787
788class ClearWeakByteRef: public ScanAddress
789{
790public:
791    ClearWeakByteRef() {}
792    virtual PolyObject *ScanObjectAddress(PolyObject *base) { return base; }
793    virtual void ScanAddressesInObject(PolyObject *base, POLYUNSIGNED lengthWord);
794};
795
796// Set the values of external references and clear the values of other weak byte refs.
797void ClearWeakByteRef::ScanAddressesInObject(PolyObject *base, POLYUNSIGNED lengthWord)
798{
799    if (OBJ_IS_MUTABLE_OBJECT(lengthWord) && OBJ_IS_BYTE_OBJECT(lengthWord) && OBJ_IS_WEAKREF_OBJECT(lengthWord))
800    {
801        POLYUNSIGNED len = OBJ_OBJECT_LENGTH(lengthWord);
802        if (len > 0) base->Set(0, PolyWord::FromSigned(0));
803        setEntryPoint(base);
804    }
805}
806
807// This is copied from the B-tree in MemMgr.  It probably should be
808// merged but will do for the moment.  It's intended to reduce the
809// cost of finding the segment for relocation.
810
811class SpaceBTree
812{
813public:
814    SpaceBTree(bool is, unsigned i = 0) : isLeaf(is), index(i) { }
815    virtual ~SpaceBTree() {}
816
817    bool isLeaf;
818    unsigned index; // The index if this is a leaf
819};
820
821// A non-leaf node in the B-tree
822class SpaceBTreeTree : public SpaceBTree
823{
824public:
825    SpaceBTreeTree();
826    virtual ~SpaceBTreeTree();
827
828    SpaceBTree *tree[256];
829};
830
831SpaceBTreeTree::SpaceBTreeTree() : SpaceBTree(false)
832{
833    for (unsigned i = 0; i < 256; i++)
834        tree[i] = 0;
835}
836
837SpaceBTreeTree::~SpaceBTreeTree()
838{
839    for (unsigned i = 0; i < 256; i++)
840        delete(tree[i]);
841}
842
843
844// This class is used to relocate addresses in areas that have been loaded.
845class LoadRelocate
846{
847public:
848    LoadRelocate(): descrs(0), targetAddresses(0), nDescrs(0), errorMessage(0), spaceTree(0) {}
849    ~LoadRelocate();
850
851    void RelocateObject(PolyObject *p);
852    void RelocateAddressAt(PolyWord *pt);
853    void AddTreeRange(SpaceBTree **t, unsigned index, uintptr_t startS, uintptr_t endS);
854
855    SavedStateSegmentDescr *descrs;
856    PolyWord **targetAddresses;
857    unsigned nDescrs;
858    const char *errorMessage;
859    SpaceBTree *spaceTree;
860};
861
862LoadRelocate::~LoadRelocate()
863{
864    if (descrs) delete[](descrs);
865    if (targetAddresses) delete[](targetAddresses);
866    delete(spaceTree);
867}
868
869// Add an entry to the space B-tree.
870void LoadRelocate::AddTreeRange(SpaceBTree **tt, unsigned index, uintptr_t startS, uintptr_t endS)
871{
872    if (*tt == 0)
873        *tt = new SpaceBTreeTree;
874    ASSERT(!(*tt)->isLeaf);
875    SpaceBTreeTree *t = (SpaceBTreeTree*)*tt;
876
877    const unsigned shift = (sizeof(void*) - 1) * 8; // Takes the high-order byte
878    uintptr_t r = startS >> shift;
879    ASSERT(r < 256);
880    const uintptr_t s = endS == 0 ? 256 : endS >> shift;
881    ASSERT(s >= r && s <= 256);
882
883    if (r == s) // Wholly within this entry
884        AddTreeRange(&(t->tree[r]), index, startS << 8, endS << 8);
885    else
886    {
887        // Deal with any remainder at the start.
888        if ((r << shift) != startS)
889        {
890            AddTreeRange(&(t->tree[r]), index, startS << 8, 0 /*End of range*/);
891            r++;
892        }
893        // Whole entries.
894        while (r < s)
895        {
896            ASSERT(t->tree[r] == 0);
897            t->tree[r] = new SpaceBTree(true, index);
898            r++;
899        }
900        // Remainder at the end.
901        if ((s << shift) != endS)
902            AddTreeRange(&(t->tree[r]), index, 0, endS << 8);
903    }
904}
905
906
907// Update the addresses in a group of words.
908void LoadRelocate::RelocateAddressAt(PolyWord *pt)
909{
910    PolyWord val = *pt;
911
912    if (val.IsTagged()) return;
913
914    // Which segment is this address in?
915    // N.B. As with SpaceForAddress we need to subtract 1 to point to the length word.
916    uintptr_t t = (uintptr_t)(val.AsStackAddr() - 1);
917    SpaceBTree *tr = spaceTree;
918
919    // Each level of the tree is either a leaf or a vector of trees.
920    unsigned j = sizeof(void *) * 8;
921    for (;;)
922    {
923        if (tr == 0) break;
924        if (tr->isLeaf) {
925            // It's in this segment: relocate it to the current position.
926            unsigned i = tr->index;
927            SavedStateSegmentDescr *descr = &descrs[i];
928            PolyWord *newAddress = targetAddresses[descr->segmentIndex];
929            ASSERT(val.AsAddress() > descr->originalAddress &&
930                val.AsAddress() <= (char*)descr->originalAddress + descr->segmentSize);
931            ASSERT(newAddress != 0);
932            byte *setAddress = (byte*)newAddress + ((char*)val.AsAddress() - (char*)descr->originalAddress);
933            *pt = PolyWord::FromCodePtr(setAddress);
934            return;
935        }
936        j -= 8;
937        tr = ((SpaceBTreeTree*)tr)->tree[(t >> j) & 0xff];
938    }
939
940    // Error: Not found.
941    errorMessage = "Unmatched address";
942}
943
944// This is based on Exporter::relocateObject but does the reverse.
945// It attempts to adjust all the addresses in the object when it has
946// been read in.
947void LoadRelocate::RelocateObject(PolyObject *p)
948{
949    if (p->IsByteObject())
950    {
951    }
952    else if (p->IsCodeObject())
953    {
954        POLYUNSIGNED constCount;
955        PolyWord *cp;
956        ASSERT(! p->IsMutable() );
957        p->GetConstSegmentForCode(cp, constCount);
958        /* Now the constant area. */
959        for (POLYUNSIGNED i = 0; i < constCount; i++) RelocateAddressAt(&(cp[i]));
960        // N.B. This does not deal with constants within the code.  These have
961        // to be handled by real relocation entries.
962    }
963    else /* Ordinary objects, essentially tuples. */
964    {
965        POLYUNSIGNED length = p->Length();
966        for (POLYUNSIGNED i = 0; i < length; i++) RelocateAddressAt(p->Offset(i));
967    }
968}
969
970// Load a saved state file.  Calls itself to handle parent files.
971bool StateLoader::LoadFile(bool isInitial, time_t requiredStamp, PolyWord tail)
972{
973    LoadRelocate relocate;
974    AutoFree<TCHAR*> thisFile(_tcsdup(fileName));
975
976    AutoClose loadFile(_tfopen(fileName, _T("rb")));
977    if ((FILE*)loadFile == NULL)
978    {
979        errorResult = "Cannot open load file";
980        errNumber = ERRORNUMBER;
981        return false;
982    }
983
984    SavedStateHeader header;
985    // Read the header and check the signature.
986    if (fread(&header, sizeof(SavedStateHeader), 1, loadFile) != 1)
987    {
988        errorResult = "Unable to load header";
989        return false;
990    }
991    if (strncmp(header.headerSignature, SAVEDSTATESIGNATURE, sizeof(header.headerSignature)) != 0)
992    {
993        errorResult = "File is not a saved state";
994        return false;
995    }
996    if (header.headerVersion != SAVEDSTATEVERSION ||
997        header.headerLength != sizeof(SavedStateHeader) ||
998        header.segmentDescrLength != sizeof(SavedStateSegmentDescr))
999    {
1000        errorResult = "Unsupported version of saved state file";
1001        return false;
1002    }
1003
1004    // Check that we have the required stamp before loading any children.
1005    // If a parent has been overwritten we could get a loop.
1006    if (! isInitial && header.timeStamp != requiredStamp)
1007    {
1008        // Time-stamps don't match.
1009        errorResult = "The parent for this saved state does not match or has been changed";
1010        return false;
1011    }
1012
1013    // Have verified that this is a reasonable saved state file.  If it isn't a
1014    // top-level file we have to load the parents first.
1015    if (header.parentNameEntry != 0)
1016    {
1017        if (isHierarchy)
1018        {
1019            // Take the file name from the list
1020            if (ML_Cons_Cell::IsNull(tail))
1021            {
1022                errorResult = "Missing parent name in argument list";
1023                return false;
1024            }
1025            ML_Cons_Cell *p = (ML_Cons_Cell *)tail.AsObjPtr();
1026            fileName = Poly_string_to_T_alloc(p->h);
1027            if (fileName == NULL)
1028            {
1029                errorResult = "Insufficient memory";
1030                errNumber = NOMEMORY;
1031                return false;
1032            }
1033            if (! LoadFile(false, header.parentTimeStamp, p->t))
1034                return false;
1035        }
1036        else
1037        {
1038            size_t toRead = header.stringTableSize-header.parentNameEntry;
1039            size_t elems = ((toRead + sizeof(TCHAR) - 1) / sizeof(TCHAR));
1040            // Always allow space for null terminator
1041            size_t roundedBytes = (elems + 1) * sizeof(TCHAR);
1042            TCHAR *newFileName = (TCHAR *)realloc(fileName, roundedBytes);
1043            if (newFileName == NULL)
1044            {
1045                errorResult = "Insufficient memory";
1046                errNumber = NOMEMORY;
1047                return false;
1048            }
1049            fileName = newFileName;
1050
1051            if (header.parentNameEntry >= header.stringTableSize /* Bad entry */ ||
1052                fseek(loadFile, header.stringTable + header.parentNameEntry, SEEK_SET) != 0 ||
1053                fread(fileName, 1, toRead, loadFile) != toRead)
1054            {
1055                errorResult = "Unable to read parent file name";
1056                return false;
1057            }
1058            fileName[elems] = 0; // Should already be null-terminated, but just in case.
1059
1060            if (! LoadFile(false, header.parentTimeStamp, TAGGED(0)))
1061                return false;
1062        }
1063
1064        ASSERT(hierarchyDepth > 0 && hierarchyTable[hierarchyDepth-1] != 0);
1065    }
1066    else // Top-level file
1067    {
1068        if (isHierarchy && ! ML_Cons_Cell::IsNull(tail))
1069        {
1070            // There should be no further file names if this is really the top.
1071            errorResult = "Too many file names in the list";
1072            return false;
1073        }
1074        if (header.parentTimeStamp != exportTimeStamp)
1075        {
1076            // Time-stamp does not match executable.
1077            errorResult =
1078                    "Saved state was exported from a different executable or the executable has changed";
1079            return false;
1080        }
1081
1082        // Any existing spaces at this level or greater must be turned
1083        // into local spaces.  We may have references from the stack to objects that
1084        // have previously been imported but otherwise these spaces are no longer
1085        // needed.
1086        gMem.DemoteImportSpaces();
1087        // Clean out the hierarchy table.
1088        for (unsigned h = 0; h < hierarchyDepth; h++)
1089        {
1090            delete(hierarchyTable[h]);
1091            hierarchyTable[h] = 0;
1092        }
1093        hierarchyDepth = 0;
1094    }
1095
1096    // Now have a valid, matching saved state.
1097    // Load the segment descriptors.
1098    relocate.nDescrs = header.segmentDescrCount;
1099    relocate.descrs = new SavedStateSegmentDescr[relocate.nDescrs];
1100
1101    if (fseek(loadFile, header.segmentDescr, SEEK_SET) != 0 ||
1102        fread(relocate.descrs, sizeof(SavedStateSegmentDescr), relocate.nDescrs, loadFile) != relocate.nDescrs)
1103    {
1104        errorResult = "Unable to read segment descriptors";
1105        return false;
1106    }
1107    {
1108        unsigned maxIndex = 0;
1109        for (unsigned i = 0; i < relocate.nDescrs; i++)
1110        {
1111            if (relocate.descrs[i].segmentIndex > maxIndex)
1112                maxIndex = relocate.descrs[i].segmentIndex;
1113            relocate.AddTreeRange(&relocate.spaceTree, i, (uintptr_t)relocate.descrs[i].originalAddress,
1114                (uintptr_t)((char*)relocate.descrs[i].originalAddress + relocate.descrs[i].segmentSize-1));
1115        }
1116        relocate.targetAddresses = new PolyWord*[maxIndex+1];
1117        for (unsigned i = 0; i <= maxIndex; i++) relocate.targetAddresses[i] = 0;
1118    }
1119
1120    // Read in and create the new segments first.  If we have problems,
1121    // in particular if we have run out of memory, then it's easier to recover.
1122    for (unsigned i = 0; i < relocate.nDescrs; i++)
1123    {
1124        SavedStateSegmentDescr *descr = &relocate.descrs[i];
1125        MemSpace *space = gMem.SpaceForIndex(descr->segmentIndex);
1126        if (space != NULL) relocate.targetAddresses[descr->segmentIndex] = space->bottom;
1127
1128        if (descr->segmentData == 0)
1129        { // No data - just an entry in the index.
1130            if (space == NULL/* ||
1131                descr->segmentSize != (size_t)((char*)space->top - (char*)space->bottom)*/)
1132            {
1133                errorResult = "Mismatch for existing memory space";
1134                return false;
1135            }
1136        }
1137        else if ((descr->segmentFlags & SSF_OVERWRITE) == 0)
1138        { // New segment.
1139            if (space != NULL)
1140            {
1141                errorResult = "Segment already exists";
1142                return false;
1143            }
1144            // Allocate memory for the new segment.
1145            size_t actualSize = descr->segmentSize;
1146            unsigned int perms = PERMISSION_READ|PERMISSION_WRITE;
1147            if (descr->segmentFlags & SSF_CODE) perms |= PERMISSION_EXEC;
1148            PolyWord *mem  = (PolyWord*)osMemoryManager->Allocate(actualSize, perms);
1149            if (mem == 0)
1150            {
1151                errorResult = "Unable to allocate memory";
1152                return false;
1153            }
1154            if (fseek(loadFile, descr->segmentData, SEEK_SET) != 0 ||
1155                fread(mem, descr->segmentSize, 1, loadFile) != 1)
1156            {
1157                errorResult = "Unable to read segment";
1158                osMemoryManager->Free(mem, descr->segmentSize);
1159                return false;
1160            }
1161            // Fill unused space to the top of the area.
1162            gMem.FillUnusedSpace(mem+descr->segmentSize/sizeof(PolyWord),
1163                (actualSize-descr->segmentSize)/sizeof(PolyWord));
1164            // At the moment we leave all segments with write access.
1165            unsigned mFlags =
1166                (descr->segmentFlags & SSF_WRITABLE ? MTF_WRITEABLE : 0) |
1167                (descr->segmentFlags & SSF_NOOVERWRITE ? MTF_NO_OVERWRITE : 0) |
1168                (descr->segmentFlags & SSF_BYTES ? MTF_BYTES : 0) |
1169                (descr->segmentFlags & SSF_CODE ? MTF_EXECUTABLE : 0);
1170            PermanentMemSpace *newSpace =
1171                gMem.NewPermanentSpace(mem, actualSize / sizeof(PolyWord), mFlags,
1172                        descr->segmentIndex, hierarchyDepth+1);
1173            relocate.targetAddresses[descr->segmentIndex] = mem;
1174            if (newSpace->isMutable && newSpace->byteOnly)
1175            {
1176                ClearWeakByteRef cwbr;
1177                cwbr.ScanAddressesInRegion(newSpace->bottom, newSpace->topPointer);
1178            }
1179        }
1180    }
1181
1182    // Now read in the mutable overwrites and relocate.
1183
1184    for (unsigned j = 0; j < relocate.nDescrs; j++)
1185    {
1186        SavedStateSegmentDescr *descr = &relocate.descrs[j];
1187        MemSpace *space = gMem.SpaceForIndex(descr->segmentIndex);
1188        ASSERT(space != NULL); // We should have created it.
1189        if (descr->segmentFlags & SSF_OVERWRITE)
1190        {
1191            if (fseek(loadFile, descr->segmentData, SEEK_SET) != 0 ||
1192                fread(space->bottom, descr->segmentSize, 1, loadFile) != 1)
1193            {
1194                errorResult = "Unable to read segment";
1195                return false;
1196            }
1197        }
1198
1199        // Relocation.
1200        if (descr->segmentData != 0)
1201        {
1202            // Adjust the addresses in the loaded segment.
1203            for (PolyWord *p = space->bottom; p < space->top; )
1204            {
1205                p++;
1206                PolyObject *obj = (PolyObject*)p;
1207                POLYUNSIGNED length = obj->Length();
1208                relocate.RelocateObject(obj);
1209                p += length;
1210            }
1211        }
1212
1213        // Process explicit relocations.
1214        // If we get errors just skip the error and continue rather than leave
1215        // everything in an unstable state.
1216        if (descr->relocations)
1217        {
1218            if (fseek(loadFile, descr->relocations, SEEK_SET) != 0)
1219            {
1220                errorResult = "Unable to read relocation segment";
1221                return false;
1222            }
1223            for (unsigned k = 0; k < descr->relocationCount; k++)
1224            {
1225                RelocationEntry reloc;
1226                if (fread(&reloc, sizeof(reloc), 1, loadFile) != 1)
1227                {
1228                    errorResult = "Unable to read relocation segment";
1229                    return false;
1230                }
1231                MemSpace *toSpace = gMem.SpaceForIndex(reloc.targetSegment);
1232                if (toSpace == NULL)
1233                {
1234                    errorResult = "Unknown space reference in relocation";
1235                    continue;
1236                }
1237                byte *setAddress = (byte*)space->bottom + reloc.relocAddress;
1238                byte *targetAddress = (byte*)toSpace->bottom + reloc.targetAddress;
1239                if (setAddress >= (byte*)space->top || targetAddress >= (byte*)toSpace->top)
1240                {
1241                    errorResult = "Bad relocation";
1242                    continue;
1243                }
1244                ScanAddress::SetConstantValue(setAddress, PolyWord::FromCodePtr(targetAddress), reloc.relKind);
1245            }
1246        }
1247    }
1248
1249    // Add an entry to the hierarchy table for this file.
1250    if (! AddHierarchyEntry(thisFile, header.timeStamp))
1251        return false;
1252
1253    return true; // Succeeded
1254}
1255
1256Handle LoadState(TaskData *taskData, bool isHierarchy, Handle hFileList)
1257// Load a saved state or a hierarchy.
1258// hFileList is a list if this is a hierarchy and a single name if it is not.
1259{
1260    StateLoader loader(isHierarchy, hFileList);
1261    // Request the main thread to do the load.  This may set the error string if it failed.
1262    processes->MakeRootRequest(taskData, &loader);
1263
1264    if (loader.errorResult != 0)
1265    {
1266        if (loader.errNumber == 0)
1267            raise_fail(taskData, loader.errorResult);
1268        else
1269        {
1270            AutoFree<char*> buff((char *)malloc(strlen(loader.errorResult) + 2 + _tcslen(loader.fileName) * sizeof(TCHAR) + 1));
1271#if (defined(_WIN32) && defined(UNICODE))
1272            sprintf(buff, "%s: %S", loader.errorResult, (TCHAR *)loader.fileName);
1273#else
1274            sprintf(buff, "%s: %s", loader.errorResult, (TCHAR *)loader.fileName);
1275#endif
1276            raise_syscall(taskData, buff, loader.errNumber);
1277        }
1278    }
1279
1280    return SAVE(TAGGED(0));
1281}
1282
1283/*
1284 *  Additional functions to provide information or change saved-state files.
1285 */
1286
1287// These functions do not affect the global state so can be executed by
1288// the ML threads directly.
1289
1290Handle ShowHierarchy(TaskData *taskData)
1291// Return the list of files in the hierarchy.
1292{
1293    Handle saved = taskData->saveVec.mark();
1294    Handle list  = SAVE(ListNull);
1295
1296    // Process this in reverse order.
1297    for (unsigned i = hierarchyDepth; i > 0; i--)
1298    {
1299        Handle value = SAVE(C_string_to_Poly(taskData, hierarchyTable[i-1]->fileName));
1300        Handle next  = alloc_and_save(taskData, sizeof(ML_Cons_Cell)/sizeof(PolyWord));
1301        DEREFLISTHANDLE(next)->h = value->Word();
1302        DEREFLISTHANDLE(next)->t = list->Word();
1303        taskData->saveVec.reset(saved);
1304        list = SAVE(next->Word());
1305    }
1306    return list;
1307}
1308
1309Handle RenameParent(TaskData *taskData, Handle args)
1310// Change the name of the immediate parent stored in a child
1311{
1312    // The name of the file to modify.
1313    AutoFree<TCHAR*> fileNameBuff(Poly_string_to_T_alloc(DEREFHANDLE(args)->Get(0)));
1314    if (fileNameBuff == NULL)
1315        raise_syscall(taskData, "Insufficient memory", NOMEMORY);
1316    // The new parent name to insert.
1317    AutoFree<TCHAR*> parentNameBuff(Poly_string_to_T_alloc(DEREFHANDLE(args)->Get(1)));
1318    if (parentNameBuff == NULL)
1319        raise_syscall(taskData, "Insufficient memory", NOMEMORY);
1320
1321    AutoClose loadFile(_tfopen(fileNameBuff, _T("r+b"))); // Open for reading and writing
1322    if ((FILE*)loadFile == NULL)
1323    {
1324        AutoFree<char*> buff((char *)malloc(23 + _tcslen(fileNameBuff) * sizeof(TCHAR) + 1));
1325#if (defined(_WIN32) && defined(UNICODE))
1326        sprintf(buff, "Cannot open load file: %S", (TCHAR *)fileNameBuff);
1327#else
1328        sprintf(buff, "Cannot open load file: %s", (TCHAR *)fileNameBuff);
1329#endif
1330        raise_syscall(taskData, buff, ERRORNUMBER);
1331    }
1332
1333    SavedStateHeader header;
1334    // Read the header and check the signature.
1335    if (fread(&header, sizeof(SavedStateHeader), 1, loadFile) != 1)
1336        raise_fail(taskData, "Unable to load header");
1337
1338    if (strncmp(header.headerSignature, SAVEDSTATESIGNATURE, sizeof(header.headerSignature)) != 0)
1339        raise_fail(taskData, "File is not a saved state");
1340
1341    if (header.headerVersion != SAVEDSTATEVERSION ||
1342        header.headerLength != sizeof(SavedStateHeader) ||
1343        header.segmentDescrLength != sizeof(SavedStateSegmentDescr))
1344    {
1345        raise_fail(taskData, "Unsupported version of saved state file");
1346    }
1347
1348    // Does this actually have a parent?
1349    if (header.parentNameEntry == 0)
1350        raise_fail(taskData, "File does not have a parent");
1351
1352    // At the moment the only entry in the string table is the parent
1353    // name so we can simply write a new one on the end of the file.
1354    // This makes the file grow slightly each time but it shouldn't be
1355    // significant.
1356    fseek(loadFile, 0, SEEK_END);
1357    header.stringTable = ftell(loadFile); // Remember where this is
1358    _fputtc(0, loadFile); // First byte of string table is zero
1359    _fputts(parentNameBuff, loadFile);
1360    _fputtc(0, loadFile); // A terminating null.
1361    header.stringTableSize = (_tcslen(parentNameBuff) + 2)*sizeof(TCHAR);
1362
1363    // Now rewind and write the header with the revised string table.
1364    fseek(loadFile, 0, SEEK_SET);
1365    fwrite(&header, sizeof(header), 1, loadFile);
1366
1367    return SAVE(TAGGED(0));
1368}
1369
1370Handle ShowParent(TaskData *taskData, Handle hFileName)
1371// Return the name of the immediate parent stored in a child
1372{
1373    AutoFree<TCHAR*> fileNameBuff(Poly_string_to_T_alloc(hFileName->Word()));
1374    if (fileNameBuff == NULL)
1375        raise_syscall(taskData, "Insufficient memory", NOMEMORY);
1376
1377    AutoClose loadFile(_tfopen(fileNameBuff, _T("rb")));
1378    if ((FILE*)loadFile == NULL)
1379    {
1380        AutoFree<char*> buff((char *)malloc(23 + _tcslen(fileNameBuff) * sizeof(TCHAR) + 1));
1381        if (buff == NULL)
1382            raise_syscall(taskData, "Insufficient memory", NOMEMORY);
1383#if (defined(_WIN32) && defined(UNICODE))
1384        sprintf(buff, "Cannot open load file: %S", (TCHAR *)fileNameBuff);
1385#else
1386        sprintf(buff, "Cannot open load file: %s", (TCHAR *)fileNameBuff);
1387#endif
1388        raise_syscall(taskData, buff, ERRORNUMBER);
1389    }
1390
1391    SavedStateHeader header;
1392    // Read the header and check the signature.
1393    if (fread(&header, sizeof(SavedStateHeader), 1, loadFile) != 1)
1394        raise_fail(taskData, "Unable to load header");
1395
1396    if (strncmp(header.headerSignature, SAVEDSTATESIGNATURE, sizeof(header.headerSignature)) != 0)
1397        raise_fail(taskData, "File is not a saved state");
1398
1399    if (header.headerVersion != SAVEDSTATEVERSION ||
1400        header.headerLength != sizeof(SavedStateHeader) ||
1401        header.segmentDescrLength != sizeof(SavedStateSegmentDescr))
1402    {
1403        raise_fail(taskData, "Unsupported version of saved state file");
1404    }
1405
1406    // Does this have a parent?
1407    if (header.parentNameEntry != 0)
1408    {
1409        size_t toRead = header.stringTableSize-header.parentNameEntry;
1410        size_t elems = ((toRead + sizeof(TCHAR) - 1) / sizeof(TCHAR));
1411        // Always allow space for null terminator
1412        size_t roundedBytes = (elems + 1) * sizeof(TCHAR);
1413        AutoFree<TCHAR*> parentFileName((TCHAR *)malloc(roundedBytes));
1414        if (parentFileName == NULL)
1415            raise_syscall(taskData, "Insufficient memory", NOMEMORY);
1416
1417        if (header.parentNameEntry >= header.stringTableSize /* Bad entry */ ||
1418            fseek(loadFile, header.stringTable + header.parentNameEntry, SEEK_SET) != 0 ||
1419            fread(parentFileName, 1, toRead, loadFile) != toRead)
1420        {
1421            raise_fail(taskData, "Unable to read parent file name");
1422        }
1423        parentFileName[elems] = 0; // Should already be null-terminated, but just in case.
1424        // Convert the name into a Poly string and then build a "Some" value.
1425        // It's possible, although silly, to have the empty string as a parent name.
1426        Handle resVal = SAVE(C_string_to_Poly(taskData, parentFileName));
1427        Handle result = alloc_and_save(taskData, 1);
1428        DEREFHANDLE(result)->Set(0, resVal->Word());
1429        return result;
1430    }
1431    else return SAVE(NONE_VALUE);
1432}
1433
1434// Module system
1435#define MODULESIGNATURE "POLYMODU"
1436#define MODULEVERSION   2
1437
1438typedef struct _moduleHeader
1439{
1440    // These entries are primarily to check that we have a valid
1441    // saved state file before we try to interpret anything else.
1442    char        headerSignature[8];     // Should contain MODULESIGNATURE
1443    unsigned    headerVersion;          // Should contain MODULEVERSION
1444    unsigned    headerLength;           // Number of bytes in the header
1445    unsigned    segmentDescrLength;     // Number of bytes in a descriptor
1446
1447    // These entries contain the real data.
1448    off_t       segmentDescr;           // Position of segment descriptor table
1449    unsigned    segmentDescrCount;      // Number of segment descriptors in the table
1450    time_t      timeStamp;              // The time stamp for this file.
1451    time_t      executableTimeStamp;    // The time stamp for the parent executable.
1452    // Root
1453    uintptr_t   rootSegment;
1454    POLYUNSIGNED     rootOffset;
1455} ModuleHeader;
1456
1457// Store a module
1458class ModuleStorer: public MainThreadRequest
1459{
1460public:
1461    ModuleStorer(const TCHAR *file, Handle r):
1462        MainThreadRequest(MTP_STOREMODULE), fileName(file), root(r), errorMessage(0), errCode(0) {}
1463
1464    virtual void Perform();
1465
1466    const TCHAR *fileName;
1467    Handle root;
1468    const char *errorMessage;
1469    int errCode;
1470};
1471
1472class ModuleExport: public SaveStateExport
1473{
1474public:
1475    ModuleExport(): SaveStateExport(1/* Everything EXCEPT the executable. */) {}
1476    virtual void exportStore(void); // Write the data out.
1477};
1478
1479void ModuleStorer::Perform()
1480{
1481    ModuleExport exporter;
1482#if (defined(_WIN32) && defined(UNICODE))
1483    exporter.exportFile = _wfopen(fileName, L"wb");
1484#else
1485    exporter.exportFile = fopen(fileName, "wb");
1486#endif
1487    if (exporter.exportFile == NULL)
1488    {
1489        errorMessage = "Cannot open export file";
1490        errCode = ERRORNUMBER;
1491        return;
1492    }
1493    // RunExport copies everything reachable from the root, except data from
1494    // the executable because we've set the hierarchy to 1, using CopyScan.
1495    // It builds the tables in the export data structure then calls exportStore
1496    // to actually write the data.
1497    if (! root->Word().IsDataPtr())
1498    {
1499        // If we have a completely empty module the list may be null.
1500        // This needs to be dealt with at a higher level.
1501        errorMessage = "Module root is not an address";
1502        return;
1503    }
1504    exporter.RunExport(root->WordP());
1505    errorMessage = exporter.errorMessage; // This will be null unless there's been an error.
1506}
1507
1508void ModuleExport::exportStore(void)
1509{
1510    // What we need to do here is implement the export in a similar way to e.g. PECOFFExport::exportStore
1511    // This is copied from SaveRequest::Perform and should be common code.
1512    ModuleHeader modHeader;
1513    memset(&modHeader, 0, sizeof(modHeader));
1514    modHeader.headerLength = sizeof(modHeader);
1515    strncpy(modHeader.headerSignature,
1516        MODULESIGNATURE, sizeof(modHeader.headerSignature));
1517    modHeader.headerVersion = MODULEVERSION;
1518    modHeader.segmentDescrLength = sizeof(SavedStateSegmentDescr);
1519    modHeader.executableTimeStamp = exportTimeStamp;
1520    {
1521        unsigned rootArea = findArea(this->rootFunction);
1522        struct _memTableEntry *mt = &memTable[rootArea];
1523        modHeader.rootSegment = mt->mtIndex;
1524        modHeader.rootOffset = (char*)this->rootFunction - (char*)mt->mtAddr;
1525    }
1526    modHeader.timeStamp = getBuildTime();
1527    modHeader.segmentDescrCount = this->memTableEntries; // One segment for each space.
1528    // Write out the header.
1529    fwrite(&modHeader, sizeof(modHeader), 1, this->exportFile);
1530
1531    SavedStateSegmentDescr *descrs = new SavedStateSegmentDescr [this->memTableEntries];
1532    // We need an entry in the descriptor tables for each segment in the executable because
1533    // we may have relocations that refer to addresses in it.
1534    for (unsigned j = 0; j < this->memTableEntries; j++)
1535    {
1536        SavedStateSegmentDescr *thisDescr = &descrs[j];
1537        memoryTableEntry *entry = &this->memTable[j];
1538        memset(thisDescr, 0, sizeof(SavedStateSegmentDescr));
1539        thisDescr->relocationSize = sizeof(RelocationEntry);
1540        thisDescr->segmentIndex = (unsigned)entry->mtIndex;
1541        thisDescr->segmentSize = entry->mtLength; // Set this even if we don't write it.
1542        thisDescr->originalAddress = entry->mtAddr;
1543        if (entry->mtFlags & MTF_WRITEABLE)
1544        {
1545            thisDescr->segmentFlags |= SSF_WRITABLE;
1546            if (entry->mtFlags & MTF_NO_OVERWRITE)
1547                thisDescr->segmentFlags |= SSF_NOOVERWRITE;
1548            if ((entry->mtFlags & MTF_NO_OVERWRITE) == 0)
1549                thisDescr->segmentFlags |= SSF_OVERWRITE;
1550            if (entry->mtFlags & MTF_BYTES)
1551                thisDescr->segmentFlags |= SSF_BYTES;
1552        }
1553        if (entry->mtFlags & MTF_EXECUTABLE)
1554             thisDescr->segmentFlags |= SSF_CODE;
1555    }
1556    // Write out temporarily. Will be overwritten at the end.
1557    modHeader.segmentDescr = ftell(this->exportFile);
1558    fwrite(descrs, sizeof(SavedStateSegmentDescr), this->memTableEntries, this->exportFile);
1559
1560    // Write out the relocations and the data.
1561    for (unsigned k = 0; k < this->memTableEntries; k++)
1562    {
1563        SavedStateSegmentDescr *thisDescr = &descrs[k];
1564        memoryTableEntry *entry = &this->memTable[k];
1565        if (k >= newAreas) // Not permanent areas
1566        {
1567            thisDescr->relocations = ftell(this->exportFile);
1568            // Have to write this out.
1569            this->relocationCount = 0;
1570            // Create the relocation table.
1571            char *start = (char*)entry->mtAddr;
1572            char *end = start + entry->mtLength;
1573            for (PolyWord *p = (PolyWord*)start; p < (PolyWord*)end; )
1574            {
1575                p++;
1576                PolyObject *obj = (PolyObject*)p;
1577                POLYUNSIGNED length = obj->Length();
1578                // For saved states we don't include explicit relocations except
1579                // in code but it's easier if we do for modules.
1580                if (length != 0 && obj->IsCodeObject())
1581                    machineDependent->ScanConstantsWithinCode(obj, this);
1582                relocateObject(obj);
1583                p += length;
1584            }
1585            thisDescr->relocationCount = this->relocationCount;
1586            // Write out the data.
1587            thisDescr->segmentData = ftell(exportFile);
1588            fwrite(entry->mtAddr, entry->mtLength, 1, exportFile);
1589        }
1590    }
1591
1592    // Rewrite the header and the segment tables now they're complete.
1593    fseek(exportFile, 0, SEEK_SET);
1594    fwrite(&modHeader, sizeof(modHeader), 1, exportFile);
1595    fwrite(descrs, sizeof(SavedStateSegmentDescr), this->memTableEntries, exportFile);
1596    delete[](descrs);
1597
1598    fclose(exportFile); exportFile = NULL;
1599}
1600
1601Handle StoreModule(TaskData *taskData, Handle args)
1602{
1603    TempString fileName(args->WordP()->Get(0));
1604
1605    ModuleStorer storer(fileName, SAVE(args->WordP()->Get(1)));
1606    processes->MakeRootRequest(taskData, &storer);
1607    if (storer.errorMessage)
1608        raise_syscall(taskData, storer.errorMessage, storer.errCode);
1609    return SAVE(TAGGED(0));
1610
1611}
1612
1613// Load a module.
1614class ModuleLoader: public MainThreadRequest
1615{
1616public:
1617    ModuleLoader(TaskData *taskData, const TCHAR *file):
1618        MainThreadRequest(MTP_LOADMODULE), callerTaskData(taskData), fileName(file),
1619            errorResult(NULL), errNumber(0), rootHandle(0) {}
1620
1621    virtual void Perform();
1622
1623    TaskData *callerTaskData;
1624    const TCHAR *fileName;
1625    const char *errorResult;
1626    int errNumber;
1627    Handle rootHandle;
1628};
1629
1630void ModuleLoader::Perform()
1631{
1632    AutoClose loadFile(_tfopen(fileName, _T("rb")));
1633    if ((FILE*)loadFile == NULL)
1634    {
1635        errorResult = "Cannot open load file";
1636        errNumber = ERRORNUMBER;
1637        return;
1638    }
1639
1640    ModuleHeader header;
1641    // Read the header and check the signature.
1642    if (fread(&header, sizeof(ModuleHeader), 1, loadFile) != 1)
1643    {
1644        errorResult = "Unable to load header";
1645        return;
1646    }
1647    if (strncmp(header.headerSignature, MODULESIGNATURE, sizeof(header.headerSignature)) != 0)
1648    {
1649        errorResult = "File is not a Poly/ML module";
1650        return;
1651    }
1652    if (header.headerVersion != MODULEVERSION ||
1653        header.headerLength != sizeof(ModuleHeader) ||
1654        header.segmentDescrLength != sizeof(SavedStateSegmentDescr))
1655    {
1656        errorResult = "Unsupported version of module file";
1657        return;
1658    }
1659    if (header.executableTimeStamp != exportTimeStamp)
1660    {
1661        // Time-stamp does not match executable.
1662        errorResult =
1663                "Module was exported from a different executable or the executable has changed";
1664        return;
1665    }
1666    LoadRelocate relocate;
1667    relocate.nDescrs = header.segmentDescrCount;
1668    relocate.descrs = new SavedStateSegmentDescr[relocate.nDescrs];
1669
1670    if (fseek(loadFile, header.segmentDescr, SEEK_SET) != 0 ||
1671        fread(relocate.descrs, sizeof(SavedStateSegmentDescr), relocate.nDescrs, loadFile) != relocate.nDescrs)
1672    {
1673        errorResult = "Unable to read segment descriptors";
1674        return;
1675    }
1676    {
1677        unsigned maxIndex = 0;
1678        for (unsigned i = 0; i < relocate.nDescrs; i++)
1679            if (relocate.descrs[i].segmentIndex > maxIndex)
1680                maxIndex = relocate.descrs[i].segmentIndex;
1681        relocate.targetAddresses = new PolyWord*[maxIndex+1];
1682        for (unsigned i = 0; i <= maxIndex; i++) relocate.targetAddresses[i] = 0;
1683    }
1684
1685    // Read in and create the new segments first.  If we have problems,
1686    // in particular if we have run out of memory, then it's easier to recover.
1687    for (unsigned i = 0; i < relocate.nDescrs; i++)
1688    {
1689        SavedStateSegmentDescr *descr = &relocate.descrs[i];
1690        MemSpace *space = gMem.SpaceForIndex(descr->segmentIndex);
1691
1692        if (descr->segmentData == 0)
1693        { // No data - just an entry in the index.
1694            if (space == NULL/* ||
1695                descr->segmentSize != (size_t)((char*)space->top - (char*)space->bottom)*/)
1696            {
1697                errorResult = "Mismatch for existing memory space";
1698                return;
1699            }
1700            else relocate.targetAddresses[descr->segmentIndex] = space->bottom;
1701        }
1702        else
1703        { // New segment.
1704            if (space != NULL)
1705            {
1706                errorResult = "Segment already exists";
1707                return;
1708            }
1709            // Allocate memory for the new segment.
1710            size_t actualSize = descr->segmentSize;
1711            MemSpace *space;
1712            if (descr->segmentFlags & SSF_CODE)
1713            {
1714                CodeSpace *cSpace = gMem.NewCodeSpace(actualSize);
1715                if (cSpace == 0)
1716                {
1717                    errorResult = "Unable to allocate memory";
1718                    return;
1719                }
1720                space = cSpace;
1721                cSpace->firstFree = (PolyWord*)((byte*)space->bottom + descr->segmentSize);
1722                if (cSpace->firstFree != cSpace->top)
1723                    gMem.FillUnusedSpace(cSpace->firstFree, cSpace->top - cSpace->firstFree);
1724            }
1725            else
1726            {
1727                LocalMemSpace *lSpace = gMem.NewLocalSpace(actualSize, descr->segmentFlags & SSF_WRITABLE);
1728                if (lSpace == 0)
1729                {
1730                    errorResult = "Unable to allocate memory";
1731                    return;
1732                }
1733                space = lSpace;
1734                lSpace->lowerAllocPtr = (PolyWord*)((byte*)lSpace->bottom + descr->segmentSize);
1735            }
1736            if (fseek(loadFile, descr->segmentData, SEEK_SET) != 0 ||
1737                fread(space->bottom, descr->segmentSize, 1, loadFile) != 1)
1738            {
1739                errorResult = "Unable to read segment";
1740                return;
1741            }
1742            relocate.targetAddresses[descr->segmentIndex] = space->bottom;
1743            if (space->isMutable && (descr->segmentFlags & SSF_BYTES) != 0)
1744            {
1745                ClearWeakByteRef cwbr;
1746                cwbr.ScanAddressesInRegion(space->bottom, (PolyWord*)((byte*)space->bottom + descr->segmentSize));
1747            }
1748        }
1749    }
1750    // Now deal with relocation.
1751    for (unsigned j = 0; j < relocate.nDescrs; j++)
1752    {
1753        SavedStateSegmentDescr *descr = &relocate.descrs[j];
1754        PolyWord *baseAddr = relocate.targetAddresses[descr->segmentIndex];
1755        ASSERT(baseAddr != NULL); // We should have created it.
1756        // Process explicit relocations.
1757        // If we get errors just skip the error and continue rather than leave
1758        // everything in an unstable state.
1759        if (descr->relocations)
1760        {
1761            if (fseek(loadFile, descr->relocations, SEEK_SET) != 0)
1762                errorResult = "Unable to read relocation segment";
1763            for (unsigned k = 0; k < descr->relocationCount; k++)
1764            {
1765                RelocationEntry reloc;
1766                if (fread(&reloc, sizeof(reloc), 1, loadFile) != 1)
1767                    errorResult = "Unable to read relocation segment";
1768                byte *setAddress = (byte*)baseAddr + reloc.relocAddress;
1769                byte *targetAddress = (byte*)relocate.targetAddresses[reloc.targetSegment] + reloc.targetAddress;
1770                ScanAddress::SetConstantValue(setAddress, PolyWord::FromCodePtr(targetAddress), reloc.relKind);
1771            }
1772        }
1773    }
1774
1775    // Get the root address.  Push this to the caller's save vec.  If we put the
1776    // newly created areas into local memory we could get a GC as soon as we
1777    // complete this root request.
1778    {
1779        PolyWord *baseAddr = relocate.targetAddresses[header.rootSegment];
1780        rootHandle = callerTaskData->saveVec.push((PolyObject*)((byte*)baseAddr + header.rootOffset));
1781    }
1782}
1783
1784Handle LoadModule(TaskData *taskData, Handle args)
1785{
1786    TempString fileName(args->Word());
1787    ModuleLoader loader(taskData, fileName);
1788    processes->MakeRootRequest(taskData, &loader);
1789
1790    if (loader.errorResult != 0)
1791    {
1792        if (loader.errNumber == 0)
1793            raise_fail(taskData, loader.errorResult);
1794        else
1795        {
1796            AutoFree<char*> buff((char *)malloc(strlen(loader.errorResult) + 2 + _tcslen(loader.fileName) * sizeof(TCHAR) + 1));
1797#if (defined(_WIN32) && defined(UNICODE))
1798            sprintf(buff, "%s: %S", loader.errorResult, loader.fileName);
1799#else
1800            sprintf(buff, "%s: %s", loader.errorResult, loader.fileName);
1801#endif
1802            raise_syscall(taskData, buff, loader.errNumber);
1803        }
1804    }
1805
1806    return loader.rootHandle;
1807}
1808
1809