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