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