cfad47cfa3/tads3/vmimage.cpp

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
#ifdef RCSID
2
static char RCSid[] =
3
"$Header: d:/cvsroot/tads/tads3/vmimage.cpp,v 1.3 1999/07/11 00:46:59 MJRoberts Exp $";
4
#endif
5
6
/* 
7
 *   Copyright (c) 1998, 2002 Michael J. Roberts.  All Rights Reserved.
8
 *   
9
 *   Please see the accompanying license file, LICENSE.TXT, for information
10
 *   on using and copying this software.  
11
 */
12
/*
13
Name
14
  vmimage.cpp - VM image file loader
15
Function
16
  
17
Notes
18
  
19
Modified
20
  12/12/98 MJRoberts  - Creation
21
*/
22
23
#include <stdlib.h>
24
#include <string.h>
25
#include <memory.h>
26
27
#include "os.h"
28
#include "t3std.h"
29
#include "vmtype.h"
30
#include "vminit.h"
31
#include "vmimage.h"
32
#include "vmerr.h"
33
#include "vmerrnum.h"
34
#include "vmfile.h"
35
#include "vmmeta.h"
36
#include "vmbif.h"
37
#include "vmpredef.h"
38
#include "vmrun.h"
39
#include "vmtobj.h"
40
#include "vmhost.h"
41
#include "vmobj.h"
42
#include "vmstr.h"
43
#include "vmlst.h"
44
#include "vmsave.h"
45
#include "vmrunsym.h"
46
#include "vmlookup.h"
47
#include "vmstack.h"
48
#include "tcprstyp.h"
49
#include "vmhash.h"
50
#include "vmsrcf.h"
51
#include "vmvec.h"
52
53
54
/* ------------------------------------------------------------------------ */
55
/*
56
 *   Hash table entry for the exported symbols table
57
 */
58
class CVmHashEntryExport: public CVmHashEntryCS
59
{
60
public:
61
    CVmHashEntryExport(const char *nm, size_t len, int copy_nm,
62
                       const vm_val_t *val)
63
        : CVmHashEntryCS(nm, len, copy_nm)
64
    {
65
        /* remember the value */
66
        val_ = *val;
67
    }
68
69
    /* the value of this symbol */
70
    vm_val_t val_;
71
};
72
73
/* ------------------------------------------------------------------------ */
74
/*
75
 *   Implementation of CVmImageLoaderMres interface for regular image file
76
 *   resource loading 
77
 */
78
class CVmImageLoaderMres_std: public CVmImageLoaderMres
79
{
80
public:
81
    CVmImageLoaderMres_std(int fileno, CVmHostIfc *hostifc)
82
    {
83
        fileno_ = fileno;
84
        hostifc_ = hostifc;
85
    }
86
87
    /* add a resource */
88
    void add_resource(uint32 seek_ofs, uint32 siz,
89
                      const char *res_name, size_t res_name_len)
90
    {
91
        /* call the host system interface to add the resource */
92
        hostifc_->add_resource(seek_ofs, siz, res_name, res_name_len,
93
                               fileno_);
94
    }
95
96
    /* add a local file resource link */
97
    void add_resource(const char *fname, size_t fnamelen,
98
                      const char *res_name, size_t res_name_len)
99
    {
100
        /* call the host system inetrface to add it */
101
        hostifc_->add_resource(fname, fnamelen, res_name, res_name_len);
102
    }
103
104
private:
105
    /* host interface object */
106
    CVmHostIfc *hostifc_;
107
108
    /* file number to pass to host interface */
109
    int fileno_;
110
};
111
112
113
/* ------------------------------------------------------------------------ */
114
/*
115
 *   Image file loader implementation 
116
 */
117
118
/*
119
 *   instantiation 
120
 */
121
CVmImageLoader::CVmImageLoader(CVmImageFile *fp, const char *fname,
122
                               long base_ofs)
123
{
124
    size_t i;
125
    
126
    /* remember the underlying image file interface */
127
    fp_ = fp;
128
129
    /* remember the image filename and the base seek offset */
130
    fname_ = lib_copy_str(fname);
131
    base_seek_ofs_ = base_ofs;
132
133
    /* we don't know the file version yet */
134
    ver_ = 0;
135
136
    /* allocate the pool tracking objects */
137
    for (i = 0 ; i < sizeof(pools_)/sizeof(pools_[0]) ; ++i)
138
        pools_[i] = new CVmImagePool();
139
140
    /* no metaclass dependency table loaded yet */
141
    loaded_meta_dep_ = FALSE;
142
143
    /* no function set dependency table loaded yet */
144
    loaded_funcset_dep_ = FALSE;
145
146
    /* no entrypoint loaded yet */
147
    loaded_entrypt_ = FALSE;
148
149
    /* no GSYM block yet */
150
    has_gsym_ = FALSE;
151
152
    /* no runtime symbols loaded yet */
153
    runtime_symtab_ = 0;
154
155
    /* there's no reflection LookupTable yet */
156
    reflection_symtab_ = VM_INVALID_OBJ;
157
158
    /* create the exported symbol hash table */
159
    exports_ = new CVmHashTable(64, new CVmHashFuncCS(), TRUE);
160
161
    /* create the synthesized exports hash table */
162
    synth_exports_ = new CVmHashTable(16, new CVmHashFuncCS(), TRUE);
163
164
    /* no static initializer pages yet */
165
    static_head_ = static_tail_ = 0;
166
167
    /* we don't have a static initializer code offset yet */
168
    static_cs_ofs_ = 0;
169
}
170
171
/*
172
 *   destruction 
173
 */
174
CVmImageLoader::~CVmImageLoader()
175
{
176
    size_t i;
177
178
    /* delete the pool tracking objects */
179
    for (i = 0 ; i < sizeof(pools_)/sizeof(pools_[0]) ; ++i)
180
        delete pools_[i];
181
182
    /* delete the filename string */
183
    lib_free_str(fname_);
184
185
    /* if we have our own runtime symbol table, delete it */
186
    if (runtime_symtab_ != 0)
187
        delete runtime_symtab_;
188
189
    /* delete the exports tables */
190
    delete exports_;
191
    delete synth_exports_;
192
193
    /* delete the static initializer pages */
194
    while (static_head_ != 0)
195
    {
196
        CVmStaticInitPage *nxt;
197
        
198
        /* remember the next one */
199
        nxt = static_head_->nxt_;
200
201
        /* free the image-allocated data associated with the page */
202
        fp_->free_mem(static_head_->data_);
203
204
        /* delete this one */
205
        delete static_head_;
206
207
        /* move on to the next */
208
        static_head_ = nxt;
209
    }
210
}
211
212
213
/* ------------------------------------------------------------------------ */
214
/*
215
 *   load the image 
216
 */
217
void CVmImageLoader::load(VMG0_)
218
{
219
    char buf[128];
220
    int done;
221
222
    /* set myself to be the global image loader */
223
    G_image_loader = this;
224
225
    /* 
226
     *   perform additional initialization now that we're about to load
227
     *   the image file 
228
     */
229
    vm_init_before_load(vmg_ fname_);
230
231
    /* tell the host application the name of the image file */
232
    G_host_ifc->set_image_name(fname_);
233
234
    /* read and validate the header */
235
    read_image_header();
236
237
    /* load the data blocks */
238
    for (done = FALSE ; !done ; )
239
    {
240
        ulong siz;
241
        uint flags;
242
        
243
        /* read the next data block header */
244
        fp_->copy_data(buf, 10);
245
246
        /* get the size */
247
        siz = t3rp4u(buf + 4);
248
249
        /* get the flags */
250
        flags = osrp2(buf + 8);
251
252
        /* load the block according to its type */
253
        if (block_type_is(buf, "CPPG"))
254
        {
255
            /* load the constant pool page block */
256
            load_const_pool_page(siz);
257
        }
258
        else if (block_type_is(buf, "ENTP"))
259
        {
260
            /* load the entrypoint */
261
            load_entrypt(vmg_ siz);
262
        }
263
        else if (block_type_is(buf, "OBJS"))
264
        {
265
            /* load static object block */
266
            load_static_objs(vmg_ siz);
267
        }
268
        else if (block_type_is(buf, "CPDF"))
269
        {
270
            /* load the constant pool definition block */
271
            load_const_pool_def(siz);
272
        }
273
        else if (block_type_is(buf, "MRES"))
274
        {
275
            /* 
276
             *   load the multimedia resource block (the main image file
277
             *   is always file number zero) 
278
             */
279
            CVmImageLoaderMres_std res_ifc(0, G_host_ifc);
280
            load_mres(siz, &res_ifc);
281
        }
282
        else if (block_type_is(buf, "MREL"))
283
        {
284
            /* load the multimedia resource link block */
285
            CVmImageLoaderMres_std res_ifc(0, G_host_ifc);
286
            load_mres_link(siz, &res_ifc);
287
        }
288
        else if (block_type_is(buf, "MCLD"))
289
        {
290
            /* load metaclass dependency block */
291
            load_meta_dep(vmg_ siz);
292
        }
293
        else if (block_type_is(buf, "FNSD"))
294
        {
295
            /* load function set dependency list block */
296
            load_funcset_dep(vmg_ siz);
297
        }
298
        else if (block_type_is(buf, "SYMD"))
299
        {
300
            /* load symbolic names export block */
301
            load_sym_names(vmg_ siz);
302
        }
303
        else if (block_type_is(buf, "SRCF"))
304
        {
305
            /* load the source file list */
306
            load_srcfiles(vmg_ siz);
307
        }
308
        else if (block_type_is(buf, "GSYM"))
309
        {
310
            /* load the global symbols block */
311
            load_gsym(vmg_ siz);
312
        }
313
        else if (block_type_is(buf, "MACR"))
314
        {
315
            /* load the macro symbols block */
316
            load_macros(vmg_ siz);
317
        }
318
        else if (block_type_is(buf, "MHLS"))
319
        {
320
            /* load the method header list block */
321
            load_mhls(vmg_ siz);
322
        }
323
        else if (block_type_is(buf, "SINI"))
324
        {
325
            /* load the static initializer block */
326
            load_sini(vmg_ siz);
327
        }
328
        else if (block_type_is(buf, "EOF "))
329
        {
330
            /* end of file - we can stop looking now */
331
            done = TRUE;
332
        }
333
        else
334
        {
335
            /*
336
             *   This block type is unknown, so ignore it.  If a new block
337
             *   type is added in the future, an older VM version won't
338
             *   recognize the new block, but it can still load the image
339
             *   file simply by omitting any unrecognized new blocks.
340
             *   Since the image file will not be complete in this case,
341
             *   it may not work properly.
342
             *   
343
             *   To allow for future block types which contain advisory
344
             *   data, which can safely be ignored by older VM versions,
345
             *   while also allowing for the possibility of changes that
346
             *   create incompatibilities, we have a flag in the header
347
             *   that indicates whether the block is required or not.  If
348
             *   this block is marked as mandatory, throw an error, since
349
             *   we don't recognize the block.  
350
             */
351
            if ((flags & VMIMAGE_DBF_MANDATORY) != 0)
352
                err_throw(VMERR_UNKNOWN_IMAGE_BLOCK);
353
354
            /* skip past the block */
355
            fp_->skip_ahead(siz);
356
        }
357
    }
358
359
    /* the image file is required to contain an entrypoint definition */
360
    if (!loaded_entrypt_)
361
        err_throw(VMERR_IMAGE_NO_ENTRYPT);
362
363
    /* the image file is required to contain a metaclass dependency table */
364
    if (!loaded_meta_dep_)
365
        err_throw(VMERR_IMAGE_NO_METADEP);
366
367
    /* the image is required to have a function set dependency table */
368
    if (!loaded_funcset_dep_)
369
        err_throw(VMERR_IMAGE_NO_FUNCDEP);
370
371
    /* complete the dynamic linking */
372
    do_dynamic_link(vmg0_);
373
374
    /* fix up the global symbol table metaclasses, if applicable */
375
    fix_gsym_meta(vmg0_);
376
377
    /* 
378
     *   create an IntrinsicClass instance for each metaclass that the
379
     *   image file is using and for which the compiler didn't supply its
380
     *   own IntrinsicClass object 
381
     */
382
    G_meta_table->create_intrinsic_class_instances(vmg0_);
383
384
    /* 
385
     *   Attach the code pool and constant pool to their backing stores,
386
     *   which are the pool objects we loaded from the image file. 
387
     */
388
    G_code_pool->attach_backing_store(pools_[0]);
389
    G_const_pool->attach_backing_store(pools_[1]);
390
391
    /* perform any requested post-load object initializations */
392
    G_obj_table->do_all_post_load_init(vmg0_);
393
394
    /* load external resource files if possible */
395
    if (G_host_ifc->can_add_resfiles())
396
        load_ext_resfiles(vmg0_);
397
398
    /* 
399
     *   perform additional initialization now that we've finished
400
     *   loading the image file
401
     */
402
    vm_init_after_load(vmg0_);
403
404
    /* forget the image loader */
405
    G_image_loader = 0;
406
}
407
408
409
/* ------------------------------------------------------------------------ */
410
/*
411
 *   Load external resource files associated with the image file 
412
 */
413
void CVmImageLoader::load_ext_resfiles(VMG0_)
414
{
415
    int i;
416
    char suffix_lc[4];
417
    char suffix_uc[4];
418
419
    /* set up the templates for the resource file suffix */
420
    strcpy(suffix_lc, "3r0");
421
    strcpy(suffix_uc, "3R0");
422
    
423
    /* 
424
     *   Search for resource files with the same name as the image file, but
425
     *   with the extension replaced with .3r0 through .3r9.  Try both
426
     *   lower-case and upper-case names (in that order), in case the file
427
     *   system is case-sensitive.  
428
     */
429
    for (i = 0 ; i < 10 ; ++i)
430
    {
431
        char resfile[OSFNMAX];
432
        
433
        /* substitute the current suffix number */
434
        suffix_lc[2] = suffix_uc[2] = i + '0';
435
436
        /* 
437
         *   if there's an explicit resource path, use it, otherwise use
438
         *   the same directory that contains the image file 
439
         */
440
        if (G_host_ifc->get_res_path() != 0)
441
        {
442
            /* 
443
             *   there's an explicit resource path - build the full path to
444
             *   the resource file using the resource path and the root name
445
             *   of the image file 
446
             */
447
            os_build_full_path(resfile, sizeof(resfile),
448
                               G_host_ifc->get_res_path(),
449
                               os_get_root_name(fname_));
450
        }
451
        else
452
        {
453
            /* 
454
             *   there's no resoruce path - use the image file full name,
455
             *   including any directory path information 
456
             */
457
            strcpy(resfile, fname_);
458
        }
459
460
        /* replace the old image file extension with the resource suffix */
461
        os_remext(resfile);
462
        os_addext(resfile, suffix_lc);
463
464
        /* if this file doesn't exist, try the upper-case name */
465
        if (osfacc(resfile))
466
        {
467
            /* replace the suffix with the upper-case version */
468
            os_remext(resfile);
469
            os_addext(resfile, suffix_uc);
470
        }
471
472
        /* check to see if this file exists */
473
        if (!osfacc(resfile))
474
        {
475
            int fileno;
476
            CVmFile *fp;
477
            CVmImageFile *volatile imagefp = 0;
478
            CVmImageLoader *volatile loader = 0;
479
            
480
            /* ask the host system to assign a file number */
481
            fileno = G_host_ifc->add_resfile(resfile);
482
483
            /* create a file object for reading the file */
484
            fp = new CVmFile();
485
486
            err_try
487
            {
488
                CVmImageLoaderMres_std res_ifc(fileno, G_host_ifc);
489
490
                /* open the file */
491
                fp->open_read(resfile, OSFTT3IMG);
492
493
                /* set up the loader */
494
                imagefp = new CVmImageFileExt(fp);
495
                loader = new CVmImageLoader(imagefp, resfile, 0);
496
497
                /* load the resource-only file */
498
                loader->load_resource_file(&res_ifc);
499
            }
500
            err_finally
501
            {
502
                /* delete the objects we created */
503
                if (loader != 0)
504
                    delete loader;
505
                if (imagefp != 0)
506
                    delete imagefp;
507
                delete fp;
508
            }
509
            err_end;
510
        }
511
    }
512
}
513
514
/*
515
 *   Load a resource file from the current seek location in the given file
516
 *   handle 
517
 */
518
void CVmImageLoader::load_resources_from_fp(osfildef *fp,
519
                                            const char *fname,
520
                                            CVmHostIfc *hostifc)
521
{
522
    int fileno;
523
524
    /* ask the host system to assign a number to the file */
525
    fileno = hostifc->add_resfile(fname);
526
527
    /* set up a resource interface with the file number */
528
    CVmImageLoaderMres_std res_ifc(fileno, hostifc);
529
530
    err_try
531
    {
532
        /* load the resources */
533
        load_resources_from_fp(fp, fname, &res_ifc);
534
    }
535
    err_catch(exc)
536
    {
537
        /* ignore the error */
538
    }
539
    err_end;
540
}
541
542
/*
543
 *   Load a resource file from the current seek location in the given file
544
 *   handle 
545
 */
546
void CVmImageLoader::load_resources_from_fp(osfildef *fp,
547
                                            const char *fname,
548
                                            CVmImageLoaderMres *res_ifc)
549
{
550
    CVmFile *file;
551
    CVmImageFile *volatile imagefp = 0;
552
    CVmImageLoader *volatile loader = 0;
553
554
    /* create a file object for reading the file */
555
    file = new CVmFile();
556
557
    /* set up the file with our file handler */
558
    file->set_file(fp, 0);
559
    
560
    err_try
561
    {
562
        /* set up the loader */
563
        imagefp = new CVmImageFileExt(file);
564
        loader = new CVmImageLoader(imagefp, fname, 0);
565
566
        /* load the resource-only file */
567
        loader->load_resource_file(res_ifc);
568
    }
569
    err_finally
570
    {
571
        /* 
572
         *   detach our CVmFile object from the caller's file handle,
573
         *   since we want to leave the caller's file handle open 
574
         */
575
        file->detach_file();
576
        
577
        /* delete the objects we created */
578
        if (loader != 0)
579
            delete loader;
580
        if (imagefp != 0)
581
            delete imagefp;
582
        delete file;
583
    }
584
    err_end;
585
}
586
587
588
/* ------------------------------------------------------------------------ */
589
/*
590
 *   Load a resource-only file.  'fileno' is the file number assigned by
591
 *   the host application (via the add_resfile() interface).  
592
 */
593
void CVmImageLoader::load_resource_file(CVmImageLoaderMres *res_ifc)
594
{
595
    int done;
596
597
    /* read and validate the image header */
598
    read_image_header();
599
600
    /* load the blocks */
601
    for (done = FALSE ; !done ; )
602
    {
603
        ulong siz;
604
        uint flags;
605
        char buf[128];
606
607
        /* read the next data block header */
608
        fp_->copy_data(buf, 10);
609
610
        /* get the size */
611
        siz = t3rp4u(buf + 4);
612
613
        /* get the flags */
614
        flags = osrp2(buf + 8);
615
616
        /* check the block type */
617
        if (block_type_is(buf, "EOF "))
618
        {
619
            /* we're done */
620
            done = TRUE;
621
        }
622
        else if (block_type_is(buf, "MRES"))
623
        {
624
            /* load the multimedia resource block */
625
            load_mres(siz, res_ifc);
626
        }
627
        else if (block_type_is(buf, "MREL"))
628
        {
629
            /* load the multimedia resource link block */
630
            load_mres_link(siz, res_ifc);
631
        }
632
        else
633
        {
634
            /* 
635
             *   unknown block type - ignore it, unless it's mandatory, in
636
             *   which case this is an error 
637
             */
638
            if ((flags & VMIMAGE_DBF_MANDATORY) != 0)
639
                err_throw(VMERR_UNKNOWN_IMAGE_BLOCK);
640
641
            /* skip past the block */
642
            fp_->skip_ahead(siz);
643
        }
644
    }
645
}
646
647
/* ------------------------------------------------------------------------ */
648
/*
649
 *   Read and validate an image file header.
650
 */
651
void CVmImageLoader::read_image_header()
652
{
653
    char buf[128];
654
655
    /* 
656
     *   Read the header.  The header consists of the signature string, a
657
     *   UINT2 with the file format version number, 32 reserved bytes,
658
     *   then the compilation timestamp (24 bytes).  
659
     */
660
    fp_->copy_data(buf, sizeof(VMIMAGE_SIG)-1 + 2 + 32 + 24);
661
662
    /* verify the signature */
663
    if (memcmp(buf, VMIMAGE_SIG, sizeof(VMIMAGE_SIG)-1) != 0)
664
        err_throw(VMERR_NOT_AN_IMAGE_FILE);
665
666
    /* get the version number */
667
    ver_ = osrp2(buf + sizeof(VMIMAGE_SIG)-1);
668
669
    /* store the timestamp */
670
    memcpy(timestamp_, buf + sizeof(VMIMAGE_SIG)-1 + 2 + 32, 24);
671
672
    /* 
673
     *   check the version to ensure that it's within the range that this
674
     *   loader implementation supports 
675
     */
676
    if (ver_ > 1)
677
        err_throw(VMERR_IMAGE_INCOMPAT_VSN);
678
}
679
680
/* ------------------------------------------------------------------------ */
681
/*
682
 *   Execute the image 
683
 */
684
void CVmImageLoader::run(VMG_ const char *const *argv, int argc,
685
                         CVmRuntimeSymbols *global_symtab,
686
                         const char *saved_state)
687
{
688
    CVmRuntimeSymbols *orig_runtime_symtab;
689
    pool_ofs_t entry_code_ofs;
690
    int entry_code_argc;
691
    
692
    /* make sure we found a code pool definition */
693
    if (pools_[0]->vmpbs_get_page_count() < 1)
694
        err_throw(VMERR_IMAGE_NO_CODE);
695
696
    /* 
697
     *   Find the entrypoint.  If we have a saved state, call the function
698
     *   given by the exported symbol "mainRestore".  Otherwise, call the
699
     *   exported main entrypoint function.  
700
     */
701
    if (saved_state != 0)
702
    {
703
        /* 
704
         *   We're restoring a saved state file immediately on startup - call
705
         *   the exported "mainRestore" function.  If there is no such
706
         *   export, we can't run the program with an initial saved state.  
707
         */
708
        if (G_predef->main_restore_func == 0)
709
            err_throw(VMERR_NO_MAINRESTORE);
710
711
        /* use the mainRestore export */
712
        entry_code_ofs = G_predef->main_restore_func;
713
    }
714
    else
715
    {
716
        /* ordinary startup - use the exported primary entrypoint */
717
        entry_code_ofs = entrypt_;
718
    }
719
720
    /* we have no arguments to the entrypoint function yet */
721
    entry_code_argc = 0;
722
723
    /* set myself as the global image loader */
724
    G_image_loader = this;
725
726
    /* remember the original runtime symbol table */
727
    orig_runtime_symtab = runtime_symtab_;
728
729
    /* catch any errors so we restore globals on the way out */
730
    err_try
731
    {
732
        int i;
733
        vm_obj_id_t lst_obj;
734
        CVmObjList *lst;
735
736
        /* 
737
         *   if the caller gave us a runtime symbol table, use it over any
738
         *   we have already stored 
739
         */
740
        if (global_symtab != 0)
741
            runtime_symtab_ = global_symtab;
742
743
        /* create a LookupTable for the reflection symbols, if we have any */
744
        create_global_symtab_lookup_table(vmg0_);
745
746
        /* 
747
         *   if we successfully created a reflection symbol table, push it
748
         *   onto the stack - this will ensure that the object won't be
749
         *   discarded as long as the program is running 
750
         */
751
        if (reflection_symtab_ != VM_INVALID_OBJ)
752
        {
753
            /* set up an object value for the table and push it */
754
            G_stk->push()->set_obj(reflection_symtab_);
755
        }
756
757
        /* 
758
         *   run static initializers (do this after creating the symbol
759
         *   table, in case any of the initializers want to access the
760
         *   symbol table) 
761
         */
762
        run_static_init(vmg0_);
763
764
        /* if there's a saved state file to restore, push it */
765
        if (saved_state != 0)
766
        {
767
            /* create a string object for the filename, and push it */
768
            G_stk->push()->set_obj(
769
                CVmObjString::create(vmg_ FALSE,
770
                                     saved_state, strlen(saved_state)));
771
772
            /* count the extra startup argument */
773
            ++entry_code_argc;
774
        }
775
776
        /* 
777
         *   push a string object for each argument - push them onto the
778
         *   stack to ensure that they're referenced in case garbage
779
         *   collection occurs while we're working 
780
         */
781
        for (i = 0 ; i < argc ; ++i)
782
        {
783
            /* create and push a string for the argument */
784
            G_stk->push()->set_obj(
785
                CVmObjString::create(vmg_ FALSE, argv[i], strlen(argv[i])));
786
        }
787
788
        /* create a list to hold the strings */
789
        lst_obj = CVmObjList::create(vmg_ FALSE, argc);
790
        lst = (CVmObjList *)vm_objp(vmg_ lst_obj);
791
792
        /* set the list elements to the strings */
793
        for (i = 0 ; i < argc ; ++i)
794
        {
795
            vm_val_t *strp;
796
            
797
            /* 
798
             *   Get this item from the stack.  Note that the most recently
799
             *   pushed item is number 0, then number 1, and so on - the
800
             *   last argument string (at index argc) is thus number 0, and
801
             *   the first (at index 0) is number argc.  
802
             */
803
            strp = G_stk->get(argc - i - 1);
804
805
            /* set this list element */
806
            lst->cons_set_element(i, strp);
807
        }
808
809
        /* 
810
         *   we don't need the strings themselves any more, as we can reach
811
         *   them through the list, so drop the strings from the stack 
812
         */
813
        G_stk->discard(argc);
814
        
815
        /* push the list - it's the argument to the program entrypoint */
816
        G_stk->push()->set_obj(lst_obj);
817
        ++entry_code_argc;
818
819
        /* 
820
         *   Invoke the entrypoint function - it takes two arguments (the
821
         *   list of argument strings, and the name of the initial saved
822
         *   state file to restore, if any).  
823
         */
824
        G_interpreter->do_call(vmg_ 0, entry_code_ofs, entry_code_argc,
825
                               VM_INVALID_OBJ, VM_INVALID_PROP,
826
                               VM_INVALID_OBJ, VM_INVALID_OBJ,
827
                               "main entrypoint");
828
829
        /* 
830
         *   if we pushed a global symbol table LookupTable object, pop it -
831
         *   we left it on the stack to protect it from the garbage
832
         *   collector, since it's always implicitly referenced from the
833
         *   intrinsic function that retrieves the symbol table object 
834
         */
835
        if (reflection_symtab_ != VM_INVALID_OBJ)
836
            G_stk->discard();
837
    }
838
    err_finally
839
    {
840
        /* forget the image loader */
841
        G_image_loader = 0;
842
843
        /* restore the original runtime symbol table */
844
        runtime_symtab_ = orig_runtime_symtab;
845
    }
846
    err_end;
847
}
848
849
/*
850
 *   Run static initializers 
851
 */
852
void CVmImageLoader::run_static_init(VMG0_)
853
{
854
    CVmStaticInitPage *pg;
855
856
    /* run through the list of static initializers */
857
    for (pg = static_head_ ; pg != 0 ; pg = pg->nxt_)
858
    {
859
        size_t i;
860
861
        /* run through the initializers on this page */
862
        for (i = 0 ; i < pg->cnt_ ; ++i)
863
        {
864
            vm_obj_id_t obj;
865
            vm_prop_id_t prop;
866
            vm_val_t target;
867
868
            /* get this initializer's object and property ID */
869
            obj = pg->get_obj_id(i);
870
            prop = pg->get_prop_id(i);
871
            target.set_obj(obj);
872
873
            /* invoke this object.property */
874
            err_try
875
            {
876
                /* evaluate the property */
877
                G_interpreter->get_prop(vmg_ 0, &target, prop, &target, 0);
878
            }
879
            err_catch(exc)
880
            {
881
                char errbuf[512];
882
                const char *obj_name;
883
                size_t obj_len;
884
                const char *prop_name;
885
                size_t prop_len;
886
                
887
                /* get the message for the exception that occurred */
888
                CVmRun::get_exc_message(vmg_ exc, errbuf, sizeof(errbuf),
889
                                        FALSE);
890
891
                /* presume we won't find names */
892
                obj_name = prop_name = 0;
893
894
                /* find the obj and prop names in the global symbols */
895
                if (runtime_symtab_ != 0)
896
                {
897
                    /* find the object name */
898
                    obj_name = runtime_symtab_
899
                               ->find_obj_name(vmg_ obj, &obj_len);
900
901
                    /* find the property name */
902
                    prop_name = runtime_symtab_
903
                                ->find_prop_name(vmg_ prop, &prop_len);
904
                }
905
906
                /* if we didn't find the names, use placeholders */
907
                if (obj_name == 0)
908
                {
909
                    obj_name = "<unknown object>";
910
                    obj_len = strlen(obj_name);
911
                }
912
                if (prop_name == 0)
913
                {
914
                    prop_name = "<unknown property>";
915
                    prop_len = strlen(prop_name);
916
                }
917
918
                /* throw a new error for the static initializer failure */
919
                err_throw_a(VMERR_EXC_IN_STATIC_INIT, 3,
920
                            ERR_TYPE_TEXTCHAR_LEN, obj_name, obj_len,
921
                            ERR_TYPE_TEXTCHAR_LEN, prop_name, prop_len,
922
                            ERR_TYPE_CHAR, errbuf);
923
            }
924
            err_end;
925
        }
926
    }
927
}
928
929
/*
930
 *   Unload the image.  This detaches the pools from the backing stores,
931
 *   which must be done before the loaded copy of the image file can be
932
 *   deleted.  
933
 */
934
void CVmImageLoader::unload(VMG0_)
935
{
936
    /* detach the code pool from its backing store */
937
    G_code_pool->detach_backing_store();
938
939
    /* detach the constant pool from its backing store */
940
    G_const_pool->detach_backing_store();
941
}
942
943
/* ------------------------------------------------------------------------ */
944
/*
945
 *   Create a LookupTable to hold the symbols in the global symbol table 
946
 */
947
void CVmImageLoader::create_global_symtab_lookup_table(VMG0_)
948
{
949
    CVmObjLookupTable *lookup;
950
    vm_runtime_sym *sym;
951
952
    /* 
953
     *   if we don't have a runtime symbol table, we can't create the
954
     *   reflection LookupTable 
955
     */
956
    if (runtime_symtab_ == 0)
957
        return;
958
959
    /* 
960
     *   if we already have a reflection symbol table, there's no need to
961
     *   create another 
962
     */
963
    if (reflection_symtab_ != VM_INVALID_OBJ)
964
        return;
965
    
966
    /* create a LookupTable to hold the symbols */
967
    reflection_symtab_ = CVmObjLookupTable::
968
                         create(vmg_ FALSE, 256,
969
                                runtime_symtab_->get_sym_count());
970
971
    /* get the object, properly cast */
972
    lookup = (CVmObjLookupTable *)vm_objp(vmg_ reflection_symtab_);
973
974
    /* push the lookup table onto the stack for gc protection */
975
    G_stk->push()->set_obj(reflection_symtab_);
976
977
    /* run through the symbols and populate the LookupTable */
978
    for (sym = runtime_symtab_->get_head() ; sym != 0 ; sym = sym->nxt)
979
    {
980
        vm_val_t str;
981
982
        /* 
983
         *   skip intrinsic class modifier objects (for the same reason we
984
         *   filter these out of firstObj/nextObj iterations: they're not
985
         *   meaningful objects as far as the program is concerned, so
986
         *   there's no reason for the program to see them) 
987
         */
988
        if (sym->val.typ == VM_OBJ
989
            && CVmObjIntClsMod::is_intcls_mod_obj(vmg_ sym->val.val.obj))
990
        {
991
            /* it's an intrinsic class modifier - skip it */
992
            continue;
993
        }
994
        
995
        /* create a string object to hold this symbol */
996
        str.set_obj(CVmObjString::create(vmg_ FALSE, sym->sym, sym->len));
997
998
        /* 
999
         *   add it to the lookup table - the symbol string is the key, and
1000
         *   the symbol's value is the lookup entry value 
1001
         */
1002
        lookup->add_entry(vmg_ &str, &sym->val);
1003
    }
1004
1005
    /* done working - discard our gc protection */
1006
    G_stk->discard();
1007
}
1008
1009
1010
/* ------------------------------------------------------------------------ */
1011
/*
1012
 *   Load an Entrypoint (ENTP) block
1013
 */
1014
void CVmImageLoader::load_entrypt(VMG_ ulong siz)
1015
{
1016
    char buf[32];
1017
    
1018
    /* if we've already loaded an entrypoint, throw an error */
1019
    if (loaded_entrypt_)
1020
        err_throw(VMERR_IMAGE_ENTRYPT_REDEF);
1021
1022
    /* read the entrypoint offset */
1023
    read_data(buf, 16, &siz);
1024
1025
    /* set the entrypoint */
1026
    entrypt_ = t3rp4u(buf);
1027
1028
    /* set the method header size in the interpreter */
1029
    G_interpreter->set_funchdr_size(osrp2(buf+4));
1030
1031
    /* set the exception table entry size global */
1032
    G_exc_entry_size = osrp2(buf+6);
1033
1034
    /* set the debugger source line record size global */
1035
    G_line_entry_size = osrp2(buf+8);
1036
1037
    /* set the debug table header size */
1038
    G_dbg_hdr_size = osrp2(buf+10);
1039
1040
    /* set the debug local symbol header size */
1041
    G_dbg_lclsym_hdr_size = osrp2(buf+12);
1042
1043
    /* set the debug version ID */
1044
    G_dbg_fmt_vsn = osrp2(buf+14);
1045
1046
    /* note that we've loaded it */
1047
    loaded_entrypt_ = TRUE;
1048
}
1049
1050
/* ------------------------------------------------------------------------ */
1051
/*
1052
 *   Load a Constant Pool Definition (CPDF) data block 
1053
 */
1054
void CVmImageLoader::load_const_pool_def(ulong siz)
1055
{
1056
    char buf[32];
1057
    uint pool_id;
1058
    ulong page_count;
1059
    ulong page_size;
1060
    
1061
    /* read the block data */
1062
    read_data(buf, 10, &siz);
1063
1064
    /* decode the pool identifier, page count, and page size */
1065
    pool_id = osrp2(buf);
1066
    page_count = t3rp4u(buf + 2);
1067
    page_size = t3rp4u(buf + 6);
1068
1069
    /* ensure that the pool ID is valid */
1070
    if (pool_id < 1 || pool_id > sizeof(pools_)/sizeof(pools_[0]))
1071
        err_throw(VMERR_IMAGE_BAD_POOL_ID);
1072
1073
    /* adjust the pool to 0-based range */
1074
    --pool_id;
1075
1076
    /* initialize the pool */
1077
    pools_[pool_id]->init(fp_, page_count, page_size);
1078
}
1079
1080
1081
/* ------------------------------------------------------------------------ */
1082
/*
1083
 *   Load a Constant Pool Page (CPPG) data block 
1084
 */
1085
void CVmImageLoader::load_const_pool_page(ulong siz)
1086
{
1087
    char buf[32];
1088
    uint pool_id;
1089
    ulong idx;
1090
    uchar xor_mask;
1091
1092
    /* read the header */
1093
    read_data(buf, 7, &siz);
1094
1095
    /* decode the pool ID and page index */
1096
    pool_id = osrp2(buf);
1097
    idx = t3rp4u(buf + 2);
1098
    xor_mask = buf[6];
1099
1100
    /* ensure that the pool ID is valid */
1101
    if (pool_id < 1 || pool_id > sizeof(pools_)/sizeof(pools_[0]))
1102
        err_throw(VMERR_IMAGE_BAD_POOL_ID);
1103
1104
    /* adjust the pool to 0-based range */
1105
    --pool_id;
1106
1107
    /* set the page information */
1108
    pools_[pool_id]->set_page_info(idx, fp_->get_seek(), siz, xor_mask);
1109
1110
    /* 
1111
     *   Skip past the page data, which follow the header - we don't need
1112
     *   to load the page data right now, but merely note where it is for
1113
     *   later loading.  The caller may choose to load page data only as
1114
     *   required, rather than all at once, so we don't want to bring it
1115
     *   into memory right now. 
1116
     */
1117
    fp_->skip_ahead(siz);
1118
}
1119
1120
1121
/* ------------------------------------------------------------------------ */
1122
/*
1123
 *   Load a Static Object (OBJS) data block 
1124
 */
1125
void CVmImageLoader::load_static_objs(VMG_ ulong siz)
1126
{
1127
    char buf[32];
1128
    uint obj_count;
1129
    uint meta_idx;
1130
    int header_size;
1131
    int large_objs;
1132
    uint flags;
1133
    int trans;
1134
1135
    /* read the number of objects, the metaclass index, and the flags */
1136
    read_data(buf, 6, &siz);
1137
1138
    /* decode the object count and metaclass index values */
1139
    obj_count = osrp2(buf);
1140
    meta_idx = osrp2(buf + 2);
1141
1142
    /* decode the flags */
1143
    flags = osrp2(buf + 4);
1144
    large_objs = ((flags & 1) != 0);
1145
    trans = ((flags & 2) != 0);
1146
1147
    /* calculate the per-object header size */
1148
    header_size = 4 + (large_objs ? 4 : 2);
1149
1150
    /* read the objects */
1151
    for ( ; obj_count != 0 ; --obj_count)
1152
    {
1153
        vm_obj_id_t obj_id;
1154
        size_t obj_size;
1155
        const char *data_ptr;
1156
        
1157
        /* read the object ID and data size */
1158
        read_data(buf, header_size, &siz);
1159
1160
        /* get the object ID */
1161
        obj_id = (vm_obj_id_t)t3rp4u(buf);
1162
1163
        /* get the size */
1164
        if (large_objs)
1165
        {
1166
            /* 
1167
             *   make sure the object size doesn't overflow the local
1168
             *   hardware limits 
1169
             */
1170
            if (t3rp4u(buf + 4) > (unsigned long)OSMALMAX)
1171
                err_throw(VMERR_OBJ_SIZE_OVERFLOW);
1172
            
1173
            /* read the object size */
1174
            obj_size = (size_t)t3rp4u(buf + 4);
1175
        }
1176
        else
1177
        {
1178
            /* read the 16-bit object size */
1179
            obj_size = osrp2(buf + 4);
1180
        }
1181
1182
        /* load the data, allocating space for it */
1183
        data_ptr = alloc_and_read(obj_size, &siz);
1184
1185
        /* create a new object of the required metaclass */
1186
        G_meta_table->create_from_image(vmg_ meta_idx, obj_id,
1187
                                        data_ptr, obj_size);
1188
1189
        /* mark the object as transient, if appropriate */
1190
        if (trans)
1191
            G_obj_table->set_obj_transient(obj_id);
1192
    }
1193
}
1194
1195
/* ------------------------------------------------------------------------ */
1196
/*
1197
 *   Load a multi-media resource (MRES) block 
1198
 */
1199
void CVmImageLoader::load_mres(ulong siz, CVmImageLoaderMres *res_ifc)
1200
{
1201
    char buf[16];
1202
    uint entry_cnt;
1203
    long base_ofs;
1204
    uint i;
1205
1206
    /* 
1207
     *   Note the current physical seek position - each entry's seek position
1208
     *   is stored in the table of contents as an offset from this location.
1209
     *   
1210
     *   Compute the physical base offset: this is the logical base offset in
1211
     *   our image stream, plus the offset of the image stream within the
1212
     *   image file.  We need the physical base offset because the underlying
1213
     *   interpreter's resource loader works with the physical file, not the
1214
     *   logical image stream.  
1215
     */
1216
    base_ofs = fp_->get_seek() + base_seek_ofs_;
1217
    
1218
    /* read the entry count and size of the table of contents */
1219
    read_data(buf, 2, &siz);
1220
    entry_cnt = osrp2(buf);
1221
1222
    /* read the entries */
1223
    for (i = 0 ; i < entry_cnt ; ++i)
1224
    {
1225
        uint32 entry_ofs;
1226
        uint32 entry_size;
1227
        uint entry_name_len;
1228
        char name_buf[256];
1229
        char *p;
1230
        size_t rem;
1231
1232
        /* read the fixed part of the table-of-contents entry */
1233
        read_data(buf, 9, &siz);
1234
        entry_ofs = t3rp4u(buf);
1235
        entry_size = t3rp4u(buf + 4);
1236
        entry_name_len = (uchar)*(buf + 8);
1237
1238
        /* read the name */
1239
        read_data(name_buf, entry_name_len, &siz);
1240
1241
        /* null-terminate the name */
1242
        name_buf[entry_name_len] = '\0';
1243
1244
        /* XOR the bytes of the name with 0xFF */
1245
        for (p = name_buf, rem = entry_name_len ; rem != 0 ; ++p, --rem)
1246
             *p ^= 0xFF;
1247
1248
        /* add the resource to the resource interface */
1249
        res_ifc->add_resource(base_ofs + entry_ofs, entry_size,
1250
                              name_buf, entry_name_len);
1251
    }
1252
1253
    /* 
1254
     *   skip the data portion of the block, since we now have a map of
1255
     *   the data and can load individual resources on demand 
1256
     */
1257
    if (siz != 0)
1258
        fp_->skip_ahead(siz);
1259
}
1260
1261
/* ------------------------------------------------------------------------ */
1262
/*
1263
 *   Load a multi-media resource link (MREL) block 
1264
 */
1265
void CVmImageLoader::load_mres_link(ulong siz, CVmImageLoaderMres *res_ifc)
1266
{
1267
    char buf[16];
1268
    uint entry_cnt;
1269
    uint i;
1270
1271
    /* read the entry count and size of the table of contents */
1272
    read_data(buf, 2, &siz);
1273
    entry_cnt = osrp2(buf);
1274
1275
    /* read the entries */
1276
    for (i = 0 ; i < entry_cnt ; ++i)
1277
    {
1278
        char buf[1];
1279
        uint resname_len, fname_len;
1280
        char resname_buf[256], fname_buf[256];
1281
1282
        /* read the resource name */
1283
        read_data(buf, 1, &siz);
1284
        resname_len = (uchar)buf[0];
1285
        read_data(resname_buf, resname_len, &siz);
1286
        resname_buf[resname_len] = '\0';
1287
1288
        /* read the local filename this resource is linked to */
1289
        read_data(buf, 1, &siz);
1290
        fname_len = (uchar)buf[0];
1291
        read_data(fname_buf, fname_len, &siz);
1292
        fname_buf[fname_len] = '\0';
1293
1294
        /* add the resource to the resource interface */
1295
        res_ifc->add_resource(fname_buf, fname_len, resname_buf, resname_len);
1296
    }
1297
1298
    /* 
1299
     *   skip the data portion of the block, since we now have a map of
1300
     *   the data and can load individual resources on demand 
1301
     */
1302
    if (siz != 0)
1303
        fp_->skip_ahead(siz);
1304
}
1305
1306
/* ------------------------------------------------------------------------ */
1307
/* 
1308
 *   load a Metaclass Dependency block 
1309
 */
1310
void CVmImageLoader::load_meta_dep(VMG_ ulong siz)
1311
{
1312
    char buf[256 + 10];
1313
    uint entry_cnt;
1314
    uint i;
1315
1316
    /* it's an error if we've already seen a dependency block */
1317
    if (loaded_meta_dep_)
1318
        err_throw(VMERR_IMAGE_METADEP_REDEF);
1319
1320
    /* note that we've loaded a dependency block */
1321
    loaded_meta_dep_ = TRUE;
1322
1323
    /* 
1324
     *   reset the global metaclass table, in case there was anything
1325
     *   defined by a previously-loaded image - since a metaclass's index
1326
     *   in the table is implicit in the load order, we need to make sure
1327
     *   we're starting with a completely clean configuration 
1328
     */
1329
    G_meta_table->clear();
1330
    
1331
    /* read the number of entries in the table */
1332
    read_data(buf, 2, &siz);
1333
    entry_cnt = osrp2(buf);
1334
1335
    /* read the entries */
1336
    for (i = 0 ; i < entry_cnt ; ++i)
1337
    {
1338
        ushort j;
1339
        uint len;
1340
        uint prop_cnt;
1341
        uint prop_len;
1342
        vm_meta_entry_t *entry;
1343
        char *p;
1344
        ulong done_size;
1345
        long prop_start_ofs;
1346
        ulong prop_start_siz;
1347
        vm_prop_id_t max_prop;
1348
        vm_prop_id_t min_prop;
1349
1350
        /* 
1351
         *   read the record size, and calculate the value of 'siz' that
1352
         *   we expect when we've parsed this entire record - it's the
1353
         *   current size minus the size of the record (note that we have
1354
         *   to add back in the two bytes of the size record itself, since
1355
         *   'siz' will have been moved past the size record once we've
1356
         *   read it) 
1357
         */
1358
        read_data(buf, 2, &siz);
1359
        done_size = (siz + 2) - osrp2(buf);
1360
        
1361
        /* read the length of the entry */
1362
        read_data(buf, 1, &siz);
1363
        len = (uchar)buf[0];
1364
1365
        /* read the name and null-terminate it */
1366
        read_data(buf, len, &siz);
1367
        buf[len] = '\0';
1368
        p = buf + len + 1;
1369
1370
        /* read the property table information */
1371
        read_data(p, 4, &siz);
1372
        prop_cnt = osrp2(p);
1373
        prop_len = osrp2(p + 2);
1374
1375
        /* 
1376
         *   set min and max to 'invalid' to start with, in case there are
1377
         *   no properties at all in the table
1378
         */
1379
        min_prop = max_prop = VM_INVALID_PROP;
1380
1381
        /* 
1382
         *   Read the properties - pass one: find the minimum and maximum
1383
         *   property ID in the table.  Before we begin, remember where
1384
         *   the properties start in the file, so we can go back and read
1385
         *   the table again on the second pass.  
1386
         */
1387
        prop_start_ofs = fp_->get_seek();
1388
        prop_start_siz = siz;
1389
        for (j = 0 ; j < prop_cnt ; ++j)
1390
        {
1391
            vm_prop_id_t cur;
1392
            
1393
            /* read the next entry */
1394
            read_data(p, 2, &siz);
1395
1396
            /* skip any additional data in the record */
1397
            if (prop_len > 2)
1398
                skip_data(prop_len - 2, &siz);
1399
1400
            /* get the property */
1401
            cur = (vm_prop_id_t)osrp2(p);
1402
1403
            /* 
1404
             *   if this is the first property, it's the max and min so
1405
             *   far; otherwise, remember it if it's the highest or lowest
1406
             *   so far 
1407
             */
1408
            if (j == 0)
1409
            {
1410
                /* this is the first one, so note it unconditionally */
1411
                min_prop = max_prop = cur;
1412
            }
1413
            else
1414
            {
1415
                /* if it's below the current minimum, it's the new minimum */
1416
                if (cur < min_prop)
1417
                    min_prop = cur;
1418
1419
                /* if it's above the current maximum, it's the new maximum */
1420
                if (cur > max_prop)
1421
                    max_prop = cur;
1422
            }
1423
        }
1424
1425
        /* 
1426
         *   Now that we know the minimum and maximum properties in the
1427
         *   table, we can create the dependency record. 
1428
         */
1429
        G_meta_table->add_entry(buf, prop_cnt, min_prop, max_prop);
1430
1431
        /* get a pointer to my new entry */
1432
        entry = G_meta_table->get_entry(i);
1433
1434
        /*
1435
         *   Pass two: read the actual properties into the translation
1436
         *   table.  Seek back to the start of the properties in the file,
1437
         *   then read the ID's.
1438
         *   
1439
         *   Note that add_prop_xlat() expects a 1-based function table
1440
         *   index.  We are in fact reading a function table, so run our
1441
         *   function index counter from 1 to the entry count to yield the
1442
         *   proper 1-based index values.  
1443
         */
1444
        fp_->seek(prop_start_ofs);
1445
        siz = prop_start_siz;
1446
        for (j = 1 ; j <= prop_cnt ; ++j)
1447
        {
1448
            /* read the next entry */
1449
            read_data(p, 2, &siz);
1450
1451
            /* skip any additional data in the record */
1452
            if (prop_len > 2)
1453
                skip_data(prop_len - 2, &siz);
1454
1455
            /* add this entry */
1456
            entry->add_prop_xlat((vm_prop_id_t)osrp2(p), j);
1457
        }
1458
1459
        /* skip any remaining data in the record */
1460
        if (siz > done_size)
1461
            skip_data(siz - done_size, &siz);
1462
    }
1463
1464
    /* 
1465
     *   always add the root object metaclass to the table, whether the
1466
     *   image file defines it or not 
1467
     */
1468
    G_meta_table
1469
        ->add_entry_if_new(CVmObject::metaclass_reg_->get_reg_idx(),
1470
                           0, VM_INVALID_PROP, VM_INVALID_PROP);
1471
}
1472
1473
/* ------------------------------------------------------------------------ */
1474
/* 
1475
 *   load a Function Set Dependency block 
1476
 */
1477
void CVmImageLoader::load_funcset_dep(VMG_ ulong siz)
1478
{
1479
    char buf[256];
1480
    uint entry_cnt;
1481
    uint i;
1482
1483
    /* it's an error if we've already seen a dependency block */
1484
    if (loaded_funcset_dep_)
1485
        err_throw(VMERR_IMAGE_FUNCDEP_REDEF);
1486
1487
    /* note that we've loaded a dependency block */
1488
    loaded_funcset_dep_ = TRUE;
1489
1490
    /* 
1491
     *   reset the global function set dependency table, in case there was
1492
     *   anything defined by a previously-loaded image - since a function
1493
     *   set index in the table is implicit in the load order, we need to
1494
     *   make sure we're starting with a completely clean configuration 
1495
     */
1496
    G_bif_table->clear();
1497
1498
    /* read the number of entries in the table */
1499
    read_data(buf, 2, &siz);
1500
    entry_cnt = osrp2(buf);
1501
1502
    /* read the entries */
1503
    for (i = 0 ; i < entry_cnt ; ++i)
1504
    {
1505
        uint len;
1506
1507
        /* read the length of the entry */
1508
        read_data(buf, 1, &siz);
1509
        len = (uchar)buf[0];
1510
1511
        /* read the name and null-terminate it */
1512
        read_data(buf, len, &siz);
1513
        buf[len] = '\0';
1514
1515
        /* add the dependency */
1516
        G_bif_table->add_entry(buf);
1517
    }
1518
}
1519
1520
/* ------------------------------------------------------------------------ */
1521
/* 
1522
 *   load a Symbolic Names export block 
1523
 */
1524
void CVmImageLoader::load_sym_names(VMG_ ulong siz)
1525
{
1526
    char buf[256];
1527
    uint entry_cnt;
1528
    uint i;
1529
    
1530
    /* read the number of entries */
1531
    read_data(buf, 2, &siz);
1532
    entry_cnt = osrp2(buf);
1533
1534
    /* read the entries */
1535
    for (i = 0 ; i < entry_cnt ; ++i)
1536
    {
1537
        vm_val_t val;
1538
        uint namelen;
1539
1540
        /* read the data holder and name length */
1541
        read_data(buf, VMB_DATAHOLDER + 1, &siz);
1542
        vmb_get_dh(buf, &val);
1543
        namelen = (uchar)*(buf + VMB_DATAHOLDER);
1544
1545
        /* read the name */
1546
        read_data(buf, namelen, &siz);
1547
1548
        /* add this as a new entry to our exports table */
1549
        exports_->add(new CVmHashEntryExport(buf, namelen, TRUE, &val));
1550
    }
1551
}
1552
1553
/* ------------------------------------------------------------------------ */
1554
/*
1555
 *   Load a Global Symbols (GSYM) block into the runtime symbol table 
1556
 */
1557
void CVmImageLoader::load_runtime_symtab_from_gsym(VMG_ ulong siz)
1558
{
1559
    const size_t TOK_SYM_MAX_LEN = 80;
1560
    char buf[TOK_SYM_MAX_LEN + 128];
1561
    ulong cnt;
1562
1563
    /* allocate the symbol table if we haven't already done so */
1564
    if (runtime_symtab_ == 0)
1565
        runtime_symtab_ = new CVmRuntimeSymbols();
1566
1567
    /* read the symbol count */
1568
    read_data(buf, 4, &siz);
1569
    cnt = t3rp4u(buf);
1570
1571
    /* read the symbols and populate the symbol table */
1572
    for ( ; cnt != 0 ; --cnt)
1573
    {
1574
        char *sym_name;
1575
        size_t sym_len;
1576
        size_t dat_len;
1577
        tc_symtype_t sym_type;
1578
        char *dat;
1579
        vm_val_t val;
1580
1581
        /* read the symbol's length, extra data length, and type code */
1582
        read_data(buf, 6, &siz);
1583
        sym_len = osrp2(buf);
1584
        dat_len = osrp2(buf + 2);
1585
        sym_type = (tc_symtype_t)osrp2(buf + 4);
1586
1587
        /* check the lengths to make sure they don't overflow our buffer */
1588
        if (sym_len > TOK_SYM_MAX_LEN)
1589
        {
1590
            /* 
1591
             *   this symbol name is too long - skip the symbol and its
1592
             *   extra data entirely 
1593
             */
1594
            skip_data(sym_len + dat_len, &siz);
1595
1596
            /* go on to the next symbol */
1597
            continue;
1598
        }
1599
        else if (dat_len + sym_len > sizeof(buf))
1600
        {
1601
            /* 
1602
             *   the extra data block is too long - truncate the extra data
1603
             *   so that we don't overflow our buffer, but proceed anyway
1604
             *   with the truncated extra data 
1605
             */
1606
            read_data(buf, sizeof(buf), &siz);
1607
1608
            /* skip the remainder of the extra data */
1609
            skip_data(sym_len + dat_len - sizeof(buf), &siz);
1610
        }
1611
        else
1612
        {
1613
            /* read the symbol's name and its type-specific data */
1614
            read_data(buf, sym_len + dat_len, &siz);
1615
        }
1616
1617
        /* the symbol name is at the start of the buffer */
1618
        sym_name = buf;
1619
1620
        /* get the pointer to the extra data (it comes after the name) */
1621
        dat = buf + sym_len;
1622
1623
        /* create the new symbol table entry, depending on its type */
1624
        switch(sym_type)
1625
        {
1626
        case TC_SYM_FUNC:
1627
            /* set up the function pointer value */
1628
            val.set_fnptr(t3rp4u(dat));
1629
            break;
1630
1631
        case TC_SYM_OBJ:
1632
            /* set up the object value */
1633
            val.set_obj(t3rp4u(dat));
1634
            break;
1635
1636
        case TC_SYM_PROP:
1637
            /* set up the property value */
1638
            val.set_propid(osrp2(dat));
1639
            break;
1640
1641
        case TC_SYM_ENUM:
1642
            /* set up the enum value */
1643
            val.set_enum(t3rp4u(dat));
1644
            break;
1645
1646
        case TC_SYM_METACLASS:
1647
            /* set up the metaclass object value */
1648
            val.set_obj(t3rp4u(dat + 2));
1649
            break;
1650
1651
        default:
1652
            /* 
1653
             *   ignore other types; mark the value as 'empty' so we know we
1654
             *   don't have anything to add to the table 
1655
             */
1656
            val.set_empty();
1657
            break;
1658
        }
1659
1660
        /* if we found a valid type, add it to the table */
1661
        if (val.typ != VM_EMPTY)
1662
            runtime_symtab_->add_sym(sym_name, sym_len, &val);
1663
    }
1664
}
1665
1666
/* ------------------------------------------------------------------------ */
1667
/* 
1668
 *   load a Source File List block 
1669
 */
1670
void CVmImageLoader::load_srcfiles(VMG_ ulong siz)
1671
{
1672
    size_t i;
1673
    char buf[64];
1674
    size_t entry_cnt;
1675
    size_t line_size;
1676
1677
    /* 
1678
     *   if there's no source file table, we're not in debug mode and hence
1679
     *   have no use for this type of information, so skip the block 
1680
     */
1681
    if (G_srcf_table == 0)
1682
    {
1683
        /* skip the entire block in the file */
1684
        fp_->skip_ahead(siz);
1685
1686
        /* we're done */
1687
        return;
1688
    }
1689
1690
    /* clear any existing information in the table */
1691
    G_srcf_table->clear();
1692
1693
    /* read the number of entries in the table, and the line record size */
1694
    read_data(buf, 4, &siz);
1695
    entry_cnt = osrp2(buf);
1696
    line_size = osrp2(buf + 2);
1697
1698
    /* read the entries */
1699
    for (i = 0 ; i < entry_cnt ; ++i)
1700
    {
1701
        long start_pos;
1702
        long next_pos;
1703
        int orig_index;
1704
        size_t len;
1705
        CVmSrcfEntry *entry;
1706
        ulong line_cnt;
1707
        ulong skip_amt;
1708
1709
        /* note the starting file location of this record */
1710
        start_pos = fp_->get_seek();
1711
1712
        /* 
1713
         *   read the size of this file record, the original index, and the
1714
         *   length of the filename 
1715
         */
1716
        read_data(buf, 8, &siz);
1717
        orig_index = osrp2(buf + 4);
1718
        len = osrp2(buf + 6);
1719
1720
        /* 
1721
         *   calculate the file location of the start of the next block by
1722
         *   adding the starting position and size of this block 
1723
         */
1724
        next_pos = start_pos + t3rp4u(buf);
1725
1726
        /* allocate the entry */
1727
        entry = G_srcf_table->add_entry(orig_index, len);
1728
1729
        /* read the data into this entry's buffer */
1730
        read_data(entry->get_name_buf(), len, &siz);
1731
1732
        /* null-terminate the buffer */
1733
        entry->get_name_buf()[len] = '\0';
1734
1735
        /* read the number of line records */
1736
        read_data(buf, 4, &siz);
1737
        line_cnt = t3rp4u(buf);
1738
1739
        /* 
1740
         *   if the line records are too big for our buffer, or smaller than
1741
         *   our required minimum size, don't bother reading them - we'll
1742
         *   just skip them entirely 
1743
         */
1744
        if (line_size <= sizeof(buf) && line_size >= 8)
1745
        {
1746
            /* tell the source file table entry how many lines we have */
1747
            entry->alloc_line_records(line_cnt);
1748
1749
            /* read the line records */
1750
            for ( ; line_cnt != 0 ; --line_cnt)
1751
            {
1752
                ulong linenum;
1753
                ulong code_addr;
1754
1755
                /* read the record */
1756
                read_data(buf, line_size, &siz);
1757
1758
                /* get the source line number and byte code offset */
1759
                linenum = t3rp4u(buf);
1760
                code_addr = t3rp4u(buf + 4);
1761
1762
                /* add the line to the source file table entry */
1763
                entry->add_line_record(linenum, code_addr);
1764
            }
1765
        }
1766
1767
        /* skip ahead to the start of the next record */
1768
        skip_amt = next_pos - fp_->get_seek();
1769
        if (skip_amt != 0)
1770
            skip_data(skip_amt, &siz);
1771
    }
1772
}
1773
1774
/* ------------------------------------------------------------------------ */
1775
/*
1776
 *   Perform dynamic linking after loading.  
1777
 */
1778
void CVmImageLoader::do_dynamic_link(VMG0_)
1779
{
1780
    struct sym_assoc_t
1781
    {
1782
        /* the symbol name */
1783
        const char *sym;
1784
        
1785
        /* datatype */
1786
        vm_datatype_t typ;
1787
        
1788
        /* address of predef table entry for this value */
1789
        void *addr;
1790
1791
        /* 
1792
         *   flag: this is a synthetic import, which means that we don't
1793
         *   look for it in the image file, but only in the restored state 
1794
         */
1795
        int is_synthetic;
1796
    };
1797
    sym_assoc_t imports[] =
1798
    {
1799
        /* build the table by including the import list */
1800
#define VM_IMPORT_OBJ(sym, mem)  { sym, VM_OBJ,  &G_predef->mem, FALSE },
1801
#define VM_NOIMPORT_OBJ(sym, mem)  { sym, VM_OBJ,  &G_predef->mem, TRUE },
1802
#define VM_IMPORT_PROP(sym, mem) { sym, VM_PROP, &G_predef->mem, FALSE },
1803
#define VM_NOIMPORT_PROP(sym, mem) { sym, VM_PROP, &G_predef->mem, TRUE },
1804
#define VM_IMPORT_FUNC(sym, mem) { sym, VM_FUNCPTR, &G_predef->mem, FALSE },
1805
#include "vmimport.h"
1806
1807
        /* end the table */
1808
        { 0, VM_NIL, 0, FALSE }
1809
    };
1810
    const sym_assoc_t *ip;
1811
1812
    /* reset all of the predefs to undefined */
1813
    G_predef->reset();
1814
1815
    /*
1816
     *   Run through our list of symbols to import from the load file, and
1817
     *   load each one. 
1818
     */
1819
    for (ip = imports ; ip->sym != 0 ; ++ip)
1820
    {
1821
        CVmHashEntryExport *entry;
1822
1823
        /* presume we won't find it */
1824
        entry = 0;
1825
1826
        /* 
1827
         *   if it's not synthetic, look up this symbol in the image file's
1828
         *   export table (don't look up synthetic symbols, because we
1829
         *   always want to create these ourselves, not load them from an
1830
         *   image file) 
1831
         */
1832
        if (!ip->is_synthetic)
1833
        {
1834
            /* it's a regular import - look for it in the image exports */
1835
            entry = (CVmHashEntryExport *)exports_
1836
                    ->find(ip->sym, strlen(ip->sym));
1837
        }
1838
1839
        /* 
1840
         *   if we didn't find the entry in the image file's exports, look
1841
         *   in the synthesized exports, in case we're restoring state from
1842
         *   a previous session 
1843
         */
1844
        if (entry == 0)
1845
            entry = (CVmHashEntryExport *)synth_exports_
1846
                    ->find(ip->sym, strlen(ip->sym));
1847
        
1848
        /* if the symbol isn't exported, skip it and proceed to the next */
1849
        if (entry == 0)
1850
            continue;
1851
1852
        /* 
1853
         *   make sure the type of the symbol exported by the image file
1854
         *   matches the type of value we want to import for the symbol 
1855
         */
1856
        if (ip->typ != entry->val_.typ)
1857
        {
1858
            /* the exported symbol has the wrong type - throw an error */
1859
            err_throw_a(VMERR_INVAL_EXPORT_TYPE, 1, ERR_TYPE_CHAR, ip->sym);
1860
        }
1861
1862
        /* set the value according to its type */
1863
        switch(ip->typ)
1864
        {
1865
        case VM_OBJ:
1866
            /* store the object value */
1867
            *(vm_obj_id_t *)ip->addr = entry->val_.val.obj;
1868
            break;
1869
1870
        case VM_PROP:
1871
            *(vm_prop_id_t *)ip->addr = entry->val_.val.prop;
1872
            break;
1873
1874
        case VM_FUNCPTR:
1875
            *(pool_ofs_t *)ip->addr = entry->val_.val.ofs;
1876
            break;
1877
1878
        default:
1879
            /* we don't import any other types */
1880
            break;
1881
        }
1882
    }
1883
1884
    /*
1885
     *   If we don't already have one, create a vector to keep track of the
1886
     *   last property ID allocated.  We store this in a vector object so
1887
     *   that the property allocation mechanism easily integrates with the
1888
     *   undo and save/restore mechanisms.  
1889
     */
1890
    if (G_predef->last_prop_obj == VM_INVALID_OBJ)
1891
    {
1892
        /* create the object */
1893
        G_predef->last_prop_obj = CVmObjVector::create(vmg_ FALSE, 1);
1894
1895
        /* add it to the synthesized export list */
1896
        add_synth_export_obj(VM_IMPORT_NAME_LASTPROPOBJ,
1897
                             G_predef->last_prop_obj);
1898
1899
        /* 
1900
         *   set up the object with the current last property, as indicated
1901
         *   in the image file 
1902
         */
1903
        set_last_prop(vmg_ G_predef->last_prop);
1904
    }
1905
1906
    /* 
1907
     *   if the image didn't define an exceptionMessage property, allocate a
1908
     *   new property ID for it 
1909
     */
1910
    if (G_predef->rterrmsg_prop == VM_INVALID_PROP)
1911
    {
1912
        /* allocate the property ID */
1913
        G_predef->rterrmsg_prop = alloc_new_prop(vmg0_);
1914
1915
        /* add it to the synthesized export list */
1916
        add_synth_export_prop(VM_IMPORT_NAME_RTERRMSG,
1917
                              G_predef->rterrmsg_prop);
1918
    }
1919
1920
    /* 
1921
     *   Create the constant string and list placeholder objects - these are
1922
     *   never imported from the image file, but must be dynamically created
1923
     *   after loading is complete.
1924
     *   
1925
     *   Create these objects without values, since the values are
1926
     *   irrelevant - they're needed only for their type information.  
1927
     */
1928
    if (G_predef->const_str_obj == VM_INVALID_OBJ)
1929
    {
1930
        /* allocate it */
1931
        G_predef->const_str_obj = CVmObjString::create(vmg_ FALSE, 0);
1932
1933
        /* add it to the synthesized export list */
1934
        add_synth_export_obj(VM_IMPORT_NAME_CONSTSTR,
1935
                             G_predef->const_str_obj);
1936
    }
1937
    if (G_predef->const_lst_obj == VM_INVALID_OBJ)
1938
    {
1939
        /* allocate it */
1940
        G_predef->const_lst_obj = CVmObjList::create(vmg_ FALSE, (size_t)0);
1941
1942
        /* add it to the synthesized export list */
1943
        add_synth_export_obj(VM_IMPORT_NAME_CONSTLST,
1944
                             G_predef->const_lst_obj);
1945
    }
1946
}
1947
1948
1949
/* ------------------------------------------------------------------------ */
1950
/*
1951
 *   Load a static initializer list block 
1952
 */
1953
void CVmImageLoader::load_sini(VMG_ ulong siz)
1954
{
1955
    char hdr[12];
1956
    ulong siz_rem;
1957
    ulong hdr_siz;
1958
    ulong init_cnt;
1959
    ulong init_rem;
1960
    
1961
    /* 
1962
     *   read the header size, static code starting offset, and
1963
     *   initializer count 
1964
     */
1965
    fp_->copy_data(hdr, 12);
1966
    hdr_siz = t3rp4u(hdr);
1967
    static_cs_ofs_ = t3rp4u(hdr + 4);
1968
    init_cnt = t3rp4u(hdr + 8);
1969
1970
    /* skip any extra header information */
1971
    if (hdr_siz > 12)
1972
        fp_->skip_ahead(hdr_siz - 12);
1973
1974
    /* account for having read the header in our size remaining */
1975
    siz_rem = siz - hdr_siz;
1976
1977
    /* read in chunks of VM_STATIC_INIT_PAGE_MAX initializers */
1978
    for (init_rem = init_cnt ; init_rem != 0 ; )
1979
    {
1980
        size_t cur;
1981
        CVmStaticInitPage *pg;
1982
1983
        /* read the page max, or the actual number remaining if fewer */
1984
        cur = VM_STATIC_INIT_PAGE_MAX;
1985
        if (init_rem < cur)
1986
            cur = (size_t)init_rem;
1987
1988
        /* allocate the next page */
1989
        pg = new CVmStaticInitPage(cur);
1990
1991
        /* link in the page */
1992
        if (static_tail_ != 0)
1993
            static_tail_->nxt_ = pg;
1994
        else
1995
            static_head_ = pg;
1996
        static_tail_ = pg;
1997
1998
        /* read the page */
1999
        pg->data_ = fp_->alloc_and_read(cur * 6, 0, siz_rem);
2000
2001
        /* adjust our counters for the chunk */
2002
        init_rem -= cur;
2003
        siz_rem -= cur * 6;
2004
    }
2005
2006
    /* skip any data we don't use in this version */
2007
    if (siz_rem != 0)
2008
        fp_->skip_ahead(siz_rem);
2009
}
2010
2011
/* ------------------------------------------------------------------------ */
2012
/*
2013
 *   Add a synthesized object export 
2014
 */
2015
void CVmImageLoader::add_synth_export_obj(const char *nm, vm_obj_id_t obj)
2016
{
2017
    vm_val_t val;
2018
2019
    /* add the new entry with an object value */
2020
    val.set_obj(obj);
2021
    synth_exports_->add(new CVmHashEntryExport(nm, strlen(nm), TRUE, &val));
2022
}
2023
2024
/*
2025
 *   Add a synthesized property export 
2026
 */
2027
void CVmImageLoader::add_synth_export_prop(const char *nm, vm_prop_id_t prop)
2028
{
2029
    vm_val_t val;
2030
2031
    /* add the new entry with a property value */
2032
    val.set_propid(prop);
2033
    synth_exports_->add(new CVmHashEntryExport(nm, strlen(nm), TRUE, &val));
2034
}
2035
2036
/*
2037
 *   Discard all synthesized exports.  When we're resetting to the image
2038
 *   file state, or when we're about to restore a saved state, we must
2039
 *   discard the synthesized exports from the prior load so that we start
2040
 *   from the base image file state again. 
2041
 */
2042
void CVmImageLoader::discard_synth_exports()
2043
{
2044
    /* delete all of the entries in the table */
2045
    synth_exports_->delete_all_entries();
2046
}
2047
2048
/* ------------------------------------------------------------------------ */
2049
/*
2050
 *   Callback context for enumerating the synthesized export symbols for
2051
 *   saving the list to a saved state file 
2052
 */
2053
struct synth_save_cb_ctx
2054
{
2055
    /* the file to which we're writing */
2056
    CVmFile *fp;
2057
2058
    /* number of entries */
2059
    long cnt;
2060
};
2061
2062
/*
2063
 *   Save the synthesized exports to a saved state file 
2064
 */
2065
void CVmImageLoader::save_synth_exports(VMG_ CVmFile *fp)
2066
{
2067
    synth_save_cb_ctx ctx;
2068
    long pos;
2069
    long end_pos;
2070
2071
    /* set up our context */
2072
    ctx.fp = fp;
2073
    ctx.cnt = 0;
2074
2075
    /* 
2076
     *   write a placeholder count of zero, but remember where it is so we
2077
     *   can come back and fix it up later 
2078
     */
2079
    pos = fp->get_pos();
2080
    fp->write_int4(0);
2081
2082
    /* enumerate all of the entries through our save callback */
2083
    synth_exports_->enum_entries(&save_synth_export_cb, &ctx);
2084
2085
    /* go back and fix up the count, then seek back to the end */
2086
    end_pos = fp->get_pos();
2087
    fp->set_pos(pos);
2088
    fp->write_int4(ctx.cnt);
2089
    fp->set_pos(end_pos);
2090
}
2091
2092
/*
2093
 *   Synthesized export enumeration callback - save to file 
2094
 */
2095
void CVmImageLoader::save_synth_export_cb(void *ctx0, CVmHashEntry *entry0)
2096
{
2097
    synth_save_cb_ctx *ctx = (synth_save_cb_ctx *)ctx0;
2098
    CVmHashEntryExport *entry = (CVmHashEntryExport *)entry0;
2099
    char buf[VMB_DATAHOLDER];
2100
2101
    /* write this entry's length and name to the file */
2102
    ctx->fp->write_int2(entry->getlen());
2103
    ctx->fp->write_bytes(entry->getstr(), entry->getlen());
2104
2105
    /* write the value as a dataholder */
2106
    vmb_put_dh(buf, &entry->val_);
2107
    ctx->fp->write_bytes(buf, VMB_DATAHOLDER);
2108
2109
    /* count the entry */
2110
    ++(ctx->cnt);
2111
}
2112
2113
/*
2114
 *   Restore the synthesized exports from a saved state file 
2115
 */
2116
int CVmImageLoader::restore_synth_exports(VMG_ CVmFile *fp,
2117
                                          CVmObjFixup *fixups)
2118
{
2119
    long cnt;
2120
    
2121
    /* 
2122
     *   discard the old synthesized exports - we want to entirely replace
2123
     *   these with what we're about to load from the file 
2124
     */
2125
    G_image_loader->discard_synth_exports();
2126
2127
    /* read the number of synthesized exports */
2128
    cnt = fp->read_uint4();
2129
2130
    /* read the exports */
2131
    for ( ; cnt != 0 ; --cnt)
2132
    {
2133
        size_t len;
2134
        char buf[256];
2135
        char dh[VMB_DATAHOLDER];
2136
        vm_val_t val;
2137
        
2138
        /* read the length of the name and the name */
2139
        len = fp->read_uint2();
2140
        fp->read_bytes(buf, len > sizeof(buf) ? sizeof(buf) : len);
2141
2142
        /* skip any part of the name that didn't fit in the buffer */
2143
        if (len > sizeof(buf))
2144
        {
2145
            /* skip the extra bytes */
2146
            fp->set_pos(fp->get_pos() + (len - sizeof(buf)));
2147
2148
            /* limit the length we use to the buffer size */
2149
            len = sizeof(buf);
2150
        }
2151
2152
        /* read the value */
2153
        fp->read_bytes(dh, VMB_DATAHOLDER);
2154
2155
        /* if the value is an object reference, fix it up */
2156
        fixups->fix_dh(vmg_ dh);
2157
2158
        /* get the value */
2159
        vmb_get_dh(dh, &val);
2160
2161
        /* add the export */
2162
        synth_exports_->add(new CVmHashEntryExport(buf, len, TRUE, &val));
2163
    }
2164
2165
    /* success */
2166
    return 0;
2167
}
2168
2169
/* ------------------------------------------------------------------------ */
2170
/*
2171
 *   Allocate a new property ID. 
2172
 */
2173
vm_prop_id_t CVmImageLoader::alloc_new_prop(VMG0_)
2174
{
2175
    CVmObjVector *vec;
2176
    vm_val_t idx_val;
2177
    vm_val_t val;
2178
    vm_val_t new_cont;
2179
2180
    /* get the LastPropObj vector */
2181
    vec = (CVmObjVector *)vm_objp(vmg_ G_predef->last_prop_obj);
2182
2183
    /* 
2184
     *   get the element at index 1 - our last property value is always at
2185
     *   the first element 
2186
     */
2187
    idx_val.set_int(1);
2188
2189
    /* get the value from the vector */
2190
    vec->index_val(vmg_ &val, G_predef->last_prop_obj, &idx_val);
2191
2192
    /* if we've allocated every available property ID, throw an error */
2193
    if (val.val.prop == 65535)
2194
        err_throw(VMERR_OUT_OF_PROPIDS);
2195
2196
    /* allocate the new property ID by incrementing the last used ID */
2197
    ++val.val.prop;
2198
2199
    /* 
2200
     *   update the vector with the new value (this new property ID is now
2201
     *   the last property ID in use) 
2202
     */
2203
    vec->set_index_val(vmg_ &new_cont, G_predef->last_prop_obj,
2204
                       &idx_val, &val);
2205
2206
    /* return the new last ID */
2207
    return val.val.prop;
2208
}
2209
2210
/*
2211
 *   Set the last property ID.  This updates the LastPropObj synthetic
2212
 *   export object, so that the last property ID is properly integrated with
2213
 *   the undo and save/restore mechanisms.  
2214
 */
2215
void CVmImageLoader::set_last_prop(VMG_ vm_prop_id_t last_prop)
2216
{
2217
    CVmObjVector *vec;
2218
    vm_val_t idx_val;
2219
    vm_val_t new_val;
2220
    vm_val_t new_cont;
2221
2222
    /* get the LastPropObj vector */
2223
    vec = (CVmObjVector *)vm_objp(vmg_ G_predef->last_prop_obj);
2224
2225
    /* 
2226
     *   set up the values - the index is always 1, since the vector
2227
     *   contains only one element; and the value contains the new last
2228
     *   property ID 
2229
     */
2230
    idx_val.set_int(1);
2231
    new_val.set_propid(G_predef->last_prop);
2232
2233
    /* update the vector */
2234
    vec->set_index_val(vmg_ &new_cont, G_predef->last_prop_obj,
2235
                       &idx_val, &new_val);
2236
}
2237
2238
/* ------------------------------------------------------------------------ */
2239
/* 
2240
 *   Copy data from the file into a buffer, decrementing a size counter.
2241
 *   We'll throw a BLOCK_TOO_SMALL error if the read length exceeds the
2242
 *   remaining size.  
2243
 */
2244
void CVmImageLoader::read_data(char *buf, size_t read_len,
2245
                               ulong *remaining_size)
2246
{
2247
    /* ensure we have enough data left in our block */
2248
    if (read_len > *remaining_size)
2249
        err_throw(VMERR_IMAGE_BLOCK_TOO_SMALL);
2250
2251
    /* decrement the remaining size counter for the data we just read */
2252
    *remaining_size -= read_len;
2253
2254
    /* read the data */
2255
    fp_->copy_data(buf, read_len);
2256
}
2257
2258
/* 
2259
 *   skip data 
2260
 */
2261
void CVmImageLoader::skip_data(size_t skip_len, ulong *remaining_size)
2262
{
2263
    /* ensure we have enough data left in our block */
2264
    if (skip_len > *remaining_size)
2265
        err_throw(VMERR_IMAGE_BLOCK_TOO_SMALL);
2266
2267
    /* decrement the remaining size counter for the data we're skipping */
2268
    *remaining_size -= skip_len;
2269
2270
    /* skip ahead in the underlying file */
2271
    fp_->skip_ahead(skip_len);
2272
}
2273
2274
/* 
2275
 *   Allocate memory for data and read the data from the file,
2276
 *   decrementing the amount read from a size counter.  Throws
2277
 *   BLOCK_TOO_SMALL if the read length exceeds the remaining size.  
2278
 */
2279
const char *CVmImageLoader::alloc_and_read(size_t read_len,
2280
                                           ulong *remaining_size)
2281
{
2282
    const char *p;
2283
    
2284
    /* ensure we have enough data left in our block */
2285
    if (read_len > *remaining_size)
2286
        err_throw(VMERR_IMAGE_BLOCK_TOO_SMALL);
2287
2288
    /* allocate space, and read the data into the new space */
2289
    p = fp_->alloc_and_read(read_len, 0, *remaining_size);
2290
2291
    /* decrement the remaining size counter for the data we just read */
2292
    *remaining_size -= read_len;
2293
2294
    /* return the pointer to the new space */
2295
    return p;
2296
}
2297
2298
2299
/* ------------------------------------------------------------------------ */
2300
/*
2301
 *   Image Pool tracker implementation 
2302
 */
2303
2304
CVmImagePool::CVmImagePool()
2305
{
2306
    /* we don't have any information about the pool yet */
2307
    page_count_ = 0;
2308
    page_size_ = 0;
2309
    page_info_ = 0;
2310
    fp_ = 0;
2311
}
2312
2313
CVmImagePool::~CVmImagePool()
2314
{
2315
    /* if we allocated a set of page offset arrays, delete them */
2316
    if (page_info_ != 0)
2317
    {
2318
        size_t i;
2319
        size_t cnt;
2320
2321
        /* compute the number of subarrays we have */
2322
        cnt = get_subarray_count();
2323
2324
        /* delete each subarray */
2325
        for (i = 0 ; i < cnt ; ++i)
2326
            t3free(page_info_[i]);
2327
2328
        /* delete the master array */
2329
        t3free(page_info_);
2330
    }
2331
}
2332
2333
/*
2334
 *   initialize 
2335
 */
2336
void CVmImagePool::init(CVmImageFile *fp, ulong page_count, ulong page_size)
2337
{
2338
    size_t cnt;
2339
    size_t i;
2340
2341
    /* remember the page count and page size */
2342
    page_count_ = page_count;
2343
    page_size_ = page_size;
2344
2345
    /* remember the underlying image file */
2346
    fp_ = fp;
2347
2348
    /* if this pool has already been defined, throw an error */
2349
    if (page_info_ != 0)
2350
        err_throw(VMERR_IMAGE_POOL_REDEF);
2351
    
2352
    /* 
2353
     *   Allocate the main page array.  We need one entry for each
2354
     *   subarray, so figure the subarray count and allocate the
2355
     *   appropriate amount of space.  
2356
     */
2357
    cnt = get_subarray_count();
2358
    page_info_ = (CVmImagePool_pg **)t3malloc(cnt * sizeof(page_info_[0]));
2359
2360
    /* throw an error if that failed */
2361
    if (page_info_ == 0)
2362
        err_throw(VMERR_OUT_OF_MEMORY);
2363
2364
    /* clear the array */
2365
    memset(page_info_, 0, cnt * sizeof(page_info_[0]));
2366
2367
    /* allocate the subarrays */
2368
    for (i = 0 ; i < cnt ; ++i)
2369
    {
2370
        /* allocate this page, which contains file offsets */
2371
        page_info_[i] =
2372
            (CVmImagePool_pg *)t3malloc(VMIMAGE_POOL_SUBARRAY_SIZE
2373
                                        * sizeof(page_info_[i][0]));
2374
2375
        /* throw an error if that failed */
2376
        if (page_info_[i] == 0)
2377
            err_throw(VMERR_OUT_OF_MEMORY);
2378
2379
        /* clear the memory */
2380
        memset(page_info_[i], 0,
2381
               VMIMAGE_POOL_SUBARRAY_SIZE * sizeof(page_info_[i][0]));
2382
    }
2383
}
2384
2385
/*
2386
 *   Set a page seek offset 
2387
 */
2388
void CVmImagePool::set_page_info(ulong page_idx, long seek_pos,
2389
                                 size_t page_size, uchar xor_mask)
2390
{
2391
    CVmImagePool_pg *info;
2392
    
2393
    /* 
2394
     *   make sure we've allocated the page array and that this index is
2395
     *   in range -- throw an error if not 
2396
     */
2397
    if (page_info_ == 0)
2398
        err_throw(VMERR_IMAGE_POOL_BEFORE_DEF);
2399
    if (page_idx >= page_count_)
2400
        err_throw(VMERR_IMAGE_POOL_BAD_PAGE);
2401
2402
    /* get the entry for this page */
2403
    info = get_page_info(page_idx);
2404
2405
    /* set the information */
2406
    info->seek_pos = seek_pos;
2407
    info->page_size = page_size;
2408
    info->xor_mask = xor_mask;
2409
}
2410
2411
/*
2412
 *   allocate and load a page 
2413
 */
2414
const char *CVmImagePool::
2415
   vmpbs_alloc_and_load_page(pool_ofs_t ofs, size_t /*page_size*/,
2416
                             size_t load_size)
2417
{
2418
    CVmImagePool_pg *info;
2419
    
2420
    /* get the page information */
2421
    info = get_page_info_ofs(ofs);
2422
    
2423
    /* seek to the correct location in the image file */
2424
    seek_page_ofs(ofs);
2425
2426
    /* ask the underlying file to load the data */
2427
    return fp_->alloc_and_read(load_size, info->xor_mask, load_size);
2428
}
2429
2430
/*
2431
 *   load a page into the given memory block
2432
 */
2433
void CVmImagePool::vmpbs_load_page(pool_ofs_t ofs, size_t /*page_size*/,
2434
                                   size_t load_size, char *mem)
2435
{
2436
    CVmImagePool_pg *info;
2437
2438
    /* get the page information */
2439
    info = get_page_info_ofs(ofs);
2440
2441
    /* seek to the correct location in the image file */
2442
    seek_page_ofs(ofs);
2443
2444
    /* ask the underlying file to load the data */
2445
    fp_->copy_data(mem, load_size);
2446
2447
    /* apply the XOR mask to the loaded data */
2448
    apply_xor_mask(mem, load_size, info->xor_mask);
2449
}
2450
2451
/* 
2452
 *   free a page previously loaded 
2453
 */
2454
void CVmImagePool::vmpbs_free_page(const char *mem, pool_ofs_t /*ofs*/,
2455
                                   size_t /*page_size*/)
2456
{
2457
    /* tell the file to free the memory */
2458
    fp_->free_mem(mem);
2459
}
2460
2461
/*
2462
 *   Determine if the backing store pages are writable.  Let the
2463
 *   underlying file decide. 
2464
 */
2465
int CVmImagePool::vmpbs_is_writable()
2466
{
2467
    /* 
2468
     *   if the underlying file allows writing to its allocated pages,
2469
     *   allow writing 
2470
     */
2471
    return fp_->allow_write_to_alloc();
2472
}
2473
2474
/*
2475
 *   seek to the image file data for the page at the given pool offset 
2476
 */
2477
void CVmImagePool::seek_page_ofs(pool_ofs_t ofs)
2478
{
2479
    ulong page_idx;
2480
    CVmImagePool_pg *info;
2481
    
2482
    /* 
2483
     *   get the page index - pages are all of a fixed length, so we can
2484
     *   find the page index simply by dividing the offset by the page
2485
     *   size 
2486
     */
2487
    page_idx = ofs / page_size_;
2488
2489
    /* make sure the page is valid */
2490
    if (page_idx >= page_count_)
2491
        err_throw(VMERR_LOAD_BAD_PAGE_IDX);
2492
2493
    /* make sure the page has been defined */
2494
    info = get_page_info(page_idx);
2495
2496
    /* make sure this page's information has been loaded */
2497
    if (info->seek_pos == 0)
2498
        err_throw(VMERR_LOAD_UNDEF_PAGE);
2499
    
2500
    /* seek to this block's position in the file */
2501
    fp_->seek(info->seek_pos);
2502
}
2503
2504
2505
/* ------------------------------------------------------------------------ */
2506
/*
2507
 *   Image file interface - external disk file implementation 
2508
 */
2509
2510
/*
2511
 *   Delete the image file loader.  This deletes all of the memory
2512
 *   associated with loaded objects, so the image file loader must not be
2513
 *   deleted until all of the loaded root-set objects are deleted. 
2514
 */
2515
CVmImageFileExt::~CVmImageFileExt()
2516
{
2517
    CVmImageFileExt_blk *cur;
2518
    CVmImageFileExt_blk *nxt;
2519
    
2520
    /* run through the list of allocated blocks and delete each one */
2521
    for (cur = mem_head_ ; cur != 0 ; cur = nxt)
2522
    {
2523
        /* remember the next block, since we're deleting this one */
2524
        nxt = cur->nxt_;
2525
2526
        /* delete this one */
2527
        delete cur;
2528
    }
2529
}
2530
2531
2532
/*
2533
 *   copy data to the caller's buffer 
2534
 */
2535
void CVmImageFileExt::copy_data(char *buf, size_t len)
2536
{
2537
    /* read directly from the file into the caller's buffer */
2538
    fp_->read_bytes(buf, len);
2539
}
2540
2541
/*
2542
 *   allocate memory for and read data 
2543
 */
2544
const char *CVmImageFileExt::alloc_and_read(size_t len, uchar xor_mask,
2545
                                            ulong remaining_in_page)
2546
{
2547
    char *mem;
2548
    
2549
    /* allocate memory */
2550
    mem = alloc_mem(len, remaining_in_page);
2551
    if (mem == 0)
2552
        err_throw(VMERR_OUT_OF_MEMORY);
2553
2554
    /* read the data */
2555
    fp_->read_bytes(mem, len);
2556
2557
    /* if there's an XOR mask, apply it to each byte we read */
2558
    CVmImagePool::apply_xor_mask(mem, len, xor_mask);
2559
2560
    /* return the memory block */
2561
    return mem;
2562
}
2563
2564
/*
2565
 *   seek 
2566
 */
2567
void CVmImageFileExt::seek(long pos)
2568
{
2569
    /* seek to the given position in the underlying file */
2570
    fp_->set_pos(pos);
2571
}
2572
2573
/* 
2574
 *   get current seek position 
2575
 */
2576
long CVmImageFileExt::get_seek() const
2577
{
2578
    /* get the position from the underlying file */
2579
    return fp_->get_pos();
2580
}
2581
2582
/*
2583
 *   skip bytes 
2584
 */
2585
void CVmImageFileExt::skip_ahead(long len)
2586
{
2587
    /* seek ahead in the underlying file */
2588
    fp_->set_pos_from_cur(len);
2589
}
2590
2591
/*
2592
 *   Allocate data for loading 
2593
 */
2594
char *CVmImageFileExt::alloc_mem(size_t siz, ulong remaining_in_page)
2595
{
2596
    /* 
2597
     *   If the requested size is more than an eigth of our block size,
2598
     *   give the new allocation its own block.  Our blocks are intended
2599
     *   to reduce overhead for small blocks; large blocks not only won't
2600
     *   create a lot of overhead as individual "malloc" allocations, but
2601
     *   would probably leave our block underutilized by leaving a lot of
2602
     *   it free, defeating the purpose. 
2603
     */
2604
    if (siz > VMIMAGE_EXT_BLK_SIZE / 8)
2605
    {
2606
        CVmImageFileExt_blk *new_block;
2607
2608
        /* it's a big request, so give it its own block */
2609
        new_block = new CVmImageFileExt_blk(siz);
2610
2611
        /* 
2612
         *   link it at the tail of the list (we don't want to suballocate
2613
         *   anything more out of this, obviously, since we're going to
2614
         *   consume the entire block, but we do want to keep it in our
2615
         *   list so that we can track and eventually delete the memory) 
2616
         */
2617
        new_block->nxt_ = 0;
2618
        new_block->prv_ = mem_tail_;
2619
2620
        /* set the forward pointer of the previous entry */
2621
        if (mem_tail_ != 0)
2622
            mem_tail_->nxt_ = new_block;
2623
        else
2624
            mem_head_ = new_block;
2625
2626
        /* it's the new tail */
2627
        mem_tail_ = new_block;
2628
2629
        /* "suballocate" out of the new block */
2630
        return mem_tail_->suballoc(siz);
2631
    }
2632
    else
2633
    {
2634
        /* 
2635
         *   It's a small request, so suballocate it out of a block.
2636
         *   First, make sure we have space in the current block; if not,
2637
         *   allocate a new block. 
2638
         */
2639
        if (mem_head_ == 0 || siz > mem_head_->rem_)
2640
        {
2641
            CVmImageFileExt_blk *new_block;
2642
            size_t new_block_size;
2643
            
2644
            /* 
2645
             *   There's no space left in the current block - allocate a new
2646
             *   block.  Leave space in the new block for further
2647
             *   allocations, so we don't have to allocate lots of little
2648
             *   blocks; however, as an upper bound, allocate only as much
2649
             *   space as remains in the current page being read out of the
2650
             *   image file, so that we don't leave a bunch of unused space
2651
             *   after reading the last objects from the image.  
2652
             */
2653
            new_block_size = VMIMAGE_EXT_BLK_SIZE;
2654
            if (new_block_size > remaining_in_page)
2655
                new_block_size = (size_t)remaining_in_page;
2656
2657
            /* allocate the new block */
2658
            new_block = new CVmImageFileExt_blk(new_block_size);
2659
2660
            /* link it in at the head of our list */
2661
            new_block->prv_ = 0;
2662
            new_block->nxt_ = mem_head_;
2663
2664
            /* set the back pointer of the next entry */
2665
            if (mem_head_ != 0)
2666
                mem_head_->prv_ = new_block;
2667
            else
2668
                mem_tail_ = new_block;
2669
2670
            /* it's the new head */
2671
            mem_head_ = new_block;
2672
        }
2673
2674
        /* suballocate it */
2675
        return mem_head_->suballoc(siz);
2676
    }
2677
}
2678
2679
2680
/* ------------------------------------------------------------------------ */
2681
/*
2682
 *   Memory tracker for external file loader 
2683
 */
2684
2685
CVmImageFileExt_blk::CVmImageFileExt_blk(size_t siz)
2686
{
2687
    /* allocate the associated memory block */
2688
    block_ptr_ = (char *)t3malloc(siz);
2689
2690
    /* remember the amount of space remaining */
2691
    rem_ = siz;
2692
2693
    /* the next byte free is the first byte */
2694
    free_ptr_ = block_ptr_;
2695
2696
    /* nothing else is in our list yet */
2697
    nxt_ = prv_ = 0;
2698
}
2699
2700
CVmImageFileExt_blk::~CVmImageFileExt_blk()
2701
{
2702
    /* free our memory block */
2703
    t3free(block_ptr_);
2704
}
2705
2706
char *CVmImageFileExt_blk::suballoc(size_t siz)
2707
{
2708
    char *ret;
2709
    
2710
    /* if we don't have enough memory available, fail the request */
2711
    if (rem_ < siz)
2712
        return 0;
2713
2714
    /* our return value will be the current free pointer */
2715
    ret = free_ptr_;
2716
2717
    /* move the free pointer past the allocated block */
2718
    free_ptr_ += siz;
2719
2720
    /* deduct the amount we just allocated from the space available */
2721
    rem_ -= siz;
2722
2723
    /* return the memory */
2724
    return ret;
2725
}
2726
2727
/* ------------------------------------------------------------------------ */
2728
/*
2729
 *   Image file interface - memory-mapped file implementation 
2730
 */
2731
2732
/*
2733
 *   copy data to the caller's buffer 
2734
 */
2735
void CVmImageFileMem::copy_data(char *buf, size_t len)
2736
{
2737
    /* if we're past the end of the file, throw an error */
2738
    if (pos_ + len > len_)
2739
        err_throw(VMERR_READ_PAST_IMG_END);
2740
2741
    /* copy data into the caller's buffer */
2742
    memcpy(buf, mem_ + pos_, len);
2743
2744
    /* seek past the data */
2745
    pos_ += len;
2746
}
2747
2748
/*
2749
 *   allocate memory for and read data 
2750
 */
2751
const char *CVmImageFileMem::alloc_and_read(size_t len, uchar xor_mask,
2752
                                            ulong /*remaining_in_page*/)
2753
{
2754
    const char *ret;
2755
    
2756
    /* if we're past the end of the file, throw an error */
2757
    if (pos_ + len > len_)
2758
        err_throw(VMERR_READ_PAST_IMG_END);
2759
2760
    /* the data are already in memory - figure out where */
2761
    ret = mem_ + pos_;
2762
2763
    /* seek past the data */
2764
    pos_ += len;
2765
2766
    /* 
2767
     *   we can't apply an xor mask to an in-memory page - if the mask is
2768
     *   non-zero, it's an error 
2769
     */
2770
    if (xor_mask != 0)
2771
        err_throw(VMERR_XOR_MASK_BAD_IN_MEM);
2772
2773
    /* return the pointer */
2774
    return ret;
2775
}
2776
2777
/* ------------------------------------------------------------------------ */
2778
/*
2779
 *   Generic stream implementation for an image file block 
2780
 */
2781
2782
/* 
2783
 *   read bytes from the stream 
2784
 */
2785
void CVmImageFileStream::read_bytes(char *buf, size_t len)
2786
{
2787
    /* if we don't have enough data to satisfy the request, it's an error */
2788
    if (len > len_)
2789
        err_throw(VMERR_IMAGE_BLOCK_TOO_SMALL);
2790
2791
    /* deduct the space we're reading from the remaining data size */
2792
    len_ -= len;
2793
2794
    /* copy the data from the image file to the caller's buffer */
2795
    fp_->copy_data(buf, len);
2796
}
2797
2798
/*
2799
 *   write bytes to the stream 
2800
 */
2801
void CVmImageFileStream::write_bytes(const char *, size_t)
2802
{
2803
    /* this is a read-only stream, so writing isn't allowed */
2804
    err_throw(VMERR_WRITE_FILE);
2805
}