cfad47cfa3/tads3/vmimage.h

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
/* $Header: d:/cvsroot/tads/tads3/vmimage.h,v 1.3 1999/07/11 00:46:59 MJRoberts Exp $ */
2
3
/* 
4
 *   Copyright (c) 1998, 2002 Michael J. Roberts.  All Rights Reserved.
5
 *   
6
 *   Please see the accompanying license file, LICENSE.TXT, for information
7
 *   on using and copying this software.  
8
 */
9
/*
10
Name
11
  vmimage.h - VM image file loader
12
Function
13
  Loads an image file for execution.
14
15
  The VM loads and executes an image by creating an appropriate
16
  concrete CVmImageFile subclass, then creating a CVmImageLoader using
17
  the CVmImageFile object.  CVmImageLoader parses the input file, sets
18
  up page mappings for constant pools, and initializes objects found in
19
  the file.
20
21
  The lifespan of the CVmImageLoader object is the lifespan of the
22
  loaded image.  As long as the VM is executing the image, it must keep
23
  the CVmImageLoader object in existence.  The CVmImageLoader object
24
  can be deleted after the VM terminates execution of the image.
25
26
  The loader comes in two varieties: an external file loader, and a
27
  memory-mapped file loader.  The external file loader reads data from
28
  a disk file, allocating memory for the information read from the file.
29
  The memory-mapped file loader takes a chunk of memory that's already
30
  been loaded, and initializes the VM to use the pre-allocated memory,
31
  rather than allocating a separate copy of the image data.
32
33
  The memory-mapped loader is meant for systems with no external storage,
34
  such as hand-held devices.  It can also be used on systems with large,
35
  flat address spaces to speed up loading by isolating all disk access
36
  into a single bulk load of the image into memory.
37
38
  The external file loader is useful for systems with smaller address
39
  spaces, and can be used with a swapping pool implementation to allow
40
  the VM to operate when available memory is smaller than the image
41
  file.
42
Notes
43
  
44
Modified
45
  12/12/98 MJRoberts  - Creation
46
*/
47
48
#ifndef VMIMAGE_H
49
#define VMIMAGE_H
50
51
#include <memory.h>
52
53
#include "vmpool.h"
54
#include "vmglob.h"
55
#include "vmtype.h"
56
#include "vmfile.h"
57
58
59
/* ------------------------------------------------------------------------ */
60
/*
61
 *   Image file constants 
62
 */
63
64
/* 
65
 *   signature - this is at the beginning of every image file so that we
66
 *   can easily detect a completely invalid file 
67
 */
68
#define VMIMAGE_SIG "T3-image\015\012\032"
69
70
71
/* ------------------------------------------------------------------------ */
72
/*
73
 *   Data Block Flags 
74
 */
75
76
/* 
77
 *   Mandatory: this block must be recognized and loaded.  When new types
78
 *   of blocks are added in future versions, the compiler can use this
79
 *   flag to indicate whether or not an old VM can safely ignore the new
80
 *   block type.  An old VM will not be able to load a new block, since
81
 *   that new block won't have been part of the spec the VM was designed
82
 *   for; in some cases, an image file can be successfully loaded and used
83
 *   even when a particular block is ignored (possibly with the loss of
84
 *   the new feature enabled by the data in the block).  When a block can
85
 *   be ignored by an old VM without losing the ability to correctly load
86
 *   the image for the old VM's functionality, the mandatory flag will be
87
 *   set to 0; when a new block carries information without which the
88
 *   image cannot be properly loaded, the mandatory flag will be set to 1. 
89
 */
90
#define VMIMAGE_DBF_MANDATORY    0x0001
91
92
93
/* ------------------------------------------------------------------------ */
94
/*
95
 *   Constant pool image tracking structure.  For each constant pool, we
96
 *   maintain information on the locations in the image file of the pages
97
 *   in the pool.  
98
 */
99
100
/* number of entries in each subarray */
101
const size_t VMIMAGE_POOL_SUBARRAY_SIZE = 4096;
102
103
/* page information structure */
104
struct CVmImagePool_pg
105
{
106
    /* seek offset of the page */
107
    long seek_pos;
108
109
    /* size of the page */
110
    size_t page_size;
111
112
    /* XOR mask for the page */
113
    uchar xor_mask;
114
};
115
116
class CVmImagePool: public CVmPoolBackingStore
117
{
118
public:
119
    CVmImagePool();
120
    ~CVmImagePool();
121
122
    /* 
123
     *   Initialize the pool with a given number of pages and page size.
124
     *   This can only be called once, and must be called before any page
125
     *   locations can be established. 
126
     */
127
    void init(class CVmImageFile *fp, ulong page_count, ulong page_size);
128
129
    /* set a page's information */
130
    void set_page_info(ulong page_idx, long seek_pos, size_t page_size,
131
                       uchar xor_mask);
132
133
    /* apply an XOR mask to a block of bytes */
134
    static void apply_xor_mask(char *p, size_t len, uchar xor_mask)
135
    {
136
        /* 
137
         *   apply the mask only if it's non-zero - xor'ing zero with
138
         *   anything yields the original value, so we can avoid a lot of
139
         *   pointless memory traversal by checking this first 
140
         */
141
        if (xor_mask != 0)
142
        {
143
            /* xor each byte with the xor mask */
144
            for ( ; len != 0 ; --len, ++p)
145
                *p ^= xor_mask;
146
        }
147
    }
148
149
    /* -------------------------------------------------------------------- */
150
    /*
151
     *   CVmPoolBackingStore implementation 
152
     */
153
154
    /* get the total number of pages */
155
    size_t vmpbs_get_page_count() { return page_count_; }
156
157
    /* get the common page size */
158
    size_t vmpbs_get_common_page_size() { return page_size_; }
159
160
    /* get the size of a given page */
161
    size_t vmpbs_get_page_size(pool_ofs_t ofs, size_t page_size)
162
    {
163
        return get_page_info_ofs(ofs)->page_size;
164
    }
165
166
    /* allocate and load a given page */
167
    const char *vmpbs_alloc_and_load_page(pool_ofs_t ofs, size_t page_size,
168
                                          size_t load_size);
169
170
    /* free a page */
171
    void vmpbs_free_page(const char *mem, pool_ofs_t ofs, size_t page_size);
172
173
    /* load a page into a given memory block */
174
    void vmpbs_load_page(pool_ofs_t ofs, size_t page_size,
175
                         size_t load_size, char *mem);
176
177
    /* determine if the backing store pages are writable */
178
    int vmpbs_is_writable();
179
    
180
    
181
    /* -------------------------------------------------------------------- */
182
183
private:
184
    /* compute the number of subarray pages we have */
185
    size_t get_subarray_count() const
186
    {
187
        return (size_t)((page_count_ + VMIMAGE_POOL_SUBARRAY_SIZE - 1)
188
                        / VMIMAGE_POOL_SUBARRAY_SIZE);
189
    }
190
191
    /* given a page index, get the information structure at the index */
192
    CVmImagePool_pg *get_page_info(ulong idx) const
193
    {
194
        return &(page_info_[idx / VMIMAGE_POOL_SUBARRAY_SIZE]
195
                 [idx % VMIMAGE_POOL_SUBARRAY_SIZE]);
196
    }
197
198
    /* given a pool offset, get the information structure for the page */
199
    CVmImagePool_pg *get_page_info_ofs(pool_ofs_t ofs) const
200
        { return get_page_info(ofs / page_size_); }
201
202
    /* 
203
     *   Given a pool offset, seek to the image file data for the page, in
204
     *   preparation for loading the data from the image file into memory. 
205
     */
206
    void seek_page_ofs(pool_ofs_t ofs);
207
    
208
    /* number of pages in the pool */
209
    ulong page_count_;
210
211
    /* page size - each page in the pool has a common size */
212
    ulong page_size_;
213
214
    /* 
215
     *   Page seek array.  To accommodate 16-bit platforms, we keep this as a
216
     *   set of arrays, with each subarray smaller than 64k.  
217
     */
218
    CVmImagePool_pg **page_info_;
219
220
    /* underlying image file */
221
    class CVmImageFile *fp_;
222
};
223
224
/* ------------------------------------------------------------------------ */
225
/*
226
 *   Image loader.  This takes an image file interface object (see below),
227
 *   and loads the underlying image data into memory.  
228
 */
229
class CVmImageLoader
230
{
231
public:
232
    /* initialize with an image file interface */
233
    CVmImageLoader(class CVmImageFile *fp, const char *fname, long base_ofs);
234
235
    /* destruction */
236
    ~CVmImageLoader();
237
238
    /* 
239
     *   Load the image.
240
     */
241
    void load(VMG0_);
242
243
    /* 
244
     *   Load a resource-only image file.  'fileno' is the file number
245
     *   assigned by the host application (via the add_resfile()
246
     *   interface) to the resource file. 
247
     */
248
    void load_resource_file(class CVmImageLoaderMres *res_ifc);
249
250
    /* load resources from a file handle at the current seek location */
251
    static void load_resources_from_fp(osfildef *fp, const char *fname,
252
                                       class CVmHostIfc *hostifc);
253
254
    /* load resources from a file handle at the current seek location */
255
    static void load_resources_from_fp(osfildef *fp, const char *fname,
256
                                       class CVmImageLoaderMres *res_ifc);
257
258
    /* 
259
     *   Run the image.  This transfers control to the entrypoint defined in
260
     *   the image file.  This function doesn't return until the program
261
     *   defined in the image terminates its execution by returning from the
262
     *   entrypoint function or throwing an unhandled exception.
263
     *   
264
     *   If 'saved_state' is not null, it gives a null-terminated character
265
     *   string with the name of a saved state file to be restored
266
     *   immediately.  We'll pass this information to the program's
267
     *   entrypoint so it can handle the restore appropriately.
268
     *   
269
     *   The caller must create the code and constant pools before invoking
270
     *   this.  We'll set up the pools with their backing stores as loaded
271
     *   from the image file.
272
     *   
273
     *   The global_symtab argument optionally provides the global symbol
274
     *   table; if this is null, we'll use our own global symbol table that
275
     *   we loaded from the debug records, if we found any.
276
     *   
277
     *   If an unhandled exception is thrown, this function throws
278
     *   VMERR_UNHANDLED_EXC, with the exception object as the first
279
     *   parameter.  
280
     */
281
    void run(VMG_ const char *const *argv, int argc,
282
             class CVmRuntimeSymbols *global_symtab,
283
             const char *saved_state);
284
285
    /* run all static initializers */
286
    void run_static_init(VMG0_);
287
288
    /*
289
     *   Unload the image.  This should be called after execution is
290
     *   finished to disactivate the pools, which must be done before the
291
     *   image file is deleted.  
292
     */
293
    void unload(VMG0_);
294
295
    /* 
296
     *   Create a global LookupTable to hold hte symbols in the global
297
     *   symbol table.
298
     */
299
    void create_global_symtab_lookup_table(VMG0_);
300
301
    /* determine if the given block type identifiers match */
302
    static int block_type_is(const char *type1, const char *type2)
303
    {
304
        /* compare the four-byte identifiers to see if they're identical */
305
        return (memcmp(type1, type2, 4) == 0);
306
    }
307
308
    /* 
309
     *   Get the image file's timestamp.  This is a 24-byte array in the
310
     *   format "Sun Aug 01 17:05:20 1999".  The purpose of the image file
311
     *   timestamp is to provide a reasonably unique identifier that can
312
     *   be stored in a saved state file and then checked upon loading the
313
     *   file to ensure that it was created by the identical version of
314
     *   the image file.  
315
     */
316
    const char *get_timestamp() const { return &timestamp_[0]; }
317
318
    /* get the filename of the loaded image */
319
    const char *get_filename() const { return fname_; }
320
321
    /* 
322
     *   check to see if the image file has a global symbol table (GSYM
323
     *   block) 
324
     */
325
    int has_gsym() const { return has_gsym_ != 0; }
326
327
    /* 
328
     *   get the object ID of the LookupTable with the global symbol table
329
     *   for reflection purposes 
330
     */
331
    vm_obj_id_t get_reflection_symtab() const { return reflection_symtab_; }
332
333
    /*
334
     *   perform dynamic linking after loading, resetting, or restoring 
335
     */
336
    void do_dynamic_link(VMG0_);
337
338
    /* 
339
     *   delete all synthesized exports - this must be called just prior to
340
     *   resetting to image file state or loading a saved state 
341
     */
342
    void discard_synth_exports();
343
344
    /* allocate a new property ID */
345
    vm_prop_id_t alloc_new_prop(VMG0_);
346
347
    /* save/restore the synthesized export table */
348
    void save_synth_exports(VMG_ class CVmFile *fp);
349
    int restore_synth_exports(VMG_ class CVmFile *fp,
350
                              class CVmObjFixup *fixups);
351
352
    /* get the starting offset of static initializers in the code pool */
353
    ulong get_static_cs_ofs() const { return static_cs_ofs_; }
354
355
    /* get the entrypoint function's code pool offset */
356
    uint32 get_entrypt() const { return entrypt_; }
357
358
private:
359
    /* load external resource files associated with an image file */
360
    void load_ext_resfiles(VMG0_);
361
362
    /* read and verify an image file header */
363
    void read_image_header();
364
    
365
    /* load an Entrypoint block */
366
    void load_entrypt(VMG_ ulong siz);
367
368
    /* load a Static Object (OBJS) block */
369
    void load_static_objs(VMG_ ulong siz);
370
371
    /* load a Constant Pool Definition (CPDF) block */
372
    void load_const_pool_def(ulong siz);
373
374
    /* load a Constant Pool Page (CPPG) block */
375
    void load_const_pool_page(ulong siz);
376
377
    /* load a Multimedia Resource (MRES) block */
378
    void load_mres(ulong siz, class CVmImageLoaderMres *res_ifc);
379
380
    /* load a Multimedia Resource Link (MREL) block */
381
    void load_mres_link(ulong size, class CVmImageLoaderMres *res_ifc);
382
383
    /* load a Metaclass Dependency block */
384
    void load_meta_dep(VMG_ ulong siz);
385
386
    /* load a Function Set Dependency block */
387
    void load_funcset_dep(VMG_ ulong siz);
388
389
    /* load a Symbolic Names block */
390
    void load_sym_names(VMG_ ulong siz);
391
392
    /* load a Source Filenames block */
393
    void load_srcfiles(VMG_ ulong siz);
394
395
    /* load a Global Symbols block */
396
    void load_gsym(VMG_ ulong siz);
397
398
    /* load a Global Symbols (GSYM) block into the runtime symbol table */
399
    void load_runtime_symtab_from_gsym(VMG_ ulong siz);
400
401
    /* load a Macro Symbols (MACR) block */
402
    void load_macros(VMG_ ulong siz);
403
404
    /* load a Method Header List block */
405
    void load_mhls(VMG_ ulong siz);
406
407
    /* load a Static Initializer List block */
408
    void load_sini(VMG_ ulong siz);
409
410
    /* 
411
     *   Fix up the debugging global symbol table's object entries with the
412
     *   correct metaclass IDs.  This has to wait until the whole image file
413
     *   is loaded so that we're sure we have all of the objects loaded
414
     *   already. 
415
     */
416
    void fix_gsym_meta(VMG0_);
417
418
    /* 
419
     *   Copy data from the file into a buffer, decrementing a size
420
     *   counter.  We'll throw a BLOCK_TOO_SMALL error if the read length
421
     *   exceeds the remaining size. 
422
     */
423
    void read_data(char *buf, size_t read_len, ulong *remaining_size);
424
425
    /* skip data */
426
    void skip_data(size_t skipo_len, ulong *remaining_size);
427
428
    /* 
429
     *   Allocate memory for data and read the data from the file,
430
     *   decrementing the amount read from a size counter.  Throws
431
     *   BLOCK_TOO_SMALL if the read length exceeds the remaining size. 
432
     */
433
    const char *alloc_and_read(size_t read_len, ulong *remaining_size);
434
435
    /* add a resource to our resource table */
436
    void add_resource(class CVmResource *res);
437
438
    /*
439
     *   Add a symbol to the list of synthesized exports.  Each time we
440
     *   synthesize a value because we didn't find the associated symbol
441
     *   exported from the image file, we must add an entry to this table.
442
     *   On saving state, we'll save these symbols to the saved state file
443
     *   so they will be restored on load. 
444
     */
445
    void add_synth_export_obj(const char *nm, vm_obj_id_t val);
446
    void add_synth_export_prop(const char *nm, vm_prop_id_t val);
447
448
    /* set the last property ID value */
449
    void set_last_prop(VMG_ vm_prop_id_t last_prop);
450
451
    /* callback for synthesized export enumeration: save to file */
452
    static void save_synth_export_cb(void *ctx, class CVmHashEntry *entry);
453
454
    /* underlying image file interface */
455
    class CVmImageFile *fp_;
456
457
    /* image filename */
458
    char *fname_;
459
460
    /* the base seek offset of the image stream within the image file */
461
    long base_seek_ofs_;
462
463
    /* image file version number */
464
    uint ver_;
465
466
    /* image file timestamp */
467
    char timestamp_[24];
468
469
    /* code pool offset of entrypoint function */
470
    uint32 entrypt_;
471
472
    /* pool tracking objects */
473
    class CVmImagePool *pools_[2];
474
475
    /* 
476
     *   The image's exported symbols.  These are the symbols that the
477
     *   program explicitly exported for dynamic linking from the VM. 
478
     */
479
    class CVmHashTable *exports_;
480
481
    /*
482
     *   List of exports synthesized after loading by the VM.  These exports
483
     *   are not in the image file, so they must be saved in the saved state
484
     *   file so that we can reattach to the same objects and properties on
485
     *   restore.  
486
     */
487
    class CVmHashTable *synth_exports_;
488
489
    /*
490
     *   The runtime global symbol table, if we have one.  We'll build this
491
     *   from the debug records if we find any, or from the records passed
492
     *   in from the compiler when we run preinitialization.
493
     *   
494
     *   Note that the runtime global symbols are not the same as the
495
     *   exported symbols.  The exports are the symbols explicitly exported
496
     *   for dynamic linking, so that the VM can attach to particular
497
     *   objects defined in the image file.  The runtime globals are all of
498
     *   the compile-time global symbols as reflected in the debugging
499
     *   records, and are used for reflection-type operations.  
500
     */
501
    class CVmRuntimeSymbols *runtime_symtab_;
502
503
    /* object ID of LookupTable containing the global symbol table */
504
    vm_obj_id_t reflection_symtab_;
505
506
    /* head/tail of list of static initializer pages */
507
    class CVmStaticInitPage *static_head_;
508
    class CVmStaticInitPage *static_tail_;
509
510
    /* 
511
     *   starting offset in code pool of static initializer code - the
512
     *   compiler groups all static initializer code, and only static
513
     *   initializer code, above this point, so after preinit, we can omit
514
     *   all code above this point from the rewritten image file 
515
     */
516
    ulong static_cs_ofs_;
517
518
    /* flag: metaclass dependency table loaded */
519
    uint loaded_meta_dep_ : 1;
520
521
    /* flag: function set dependency table loaded */
522
    uint loaded_funcset_dep_ : 1;
523
524
    /* flag: entrypoint loaded */
525
    uint loaded_entrypt_ : 1;
526
527
    /* 
528
     *   flag: the image file has a GSYM (global symbol table), which
529
     *   implies that it was compiled for debugging 
530
     */
531
    uint has_gsym_ : 1;
532
};
533
534
535
/* ------------------------------------------------------------------------ */
536
/*
537
 *   Static initializer page.  Each page contains a fixed number of
538
 *   initializers. 
539
 */
540
const size_t VM_STATIC_INIT_PAGE_MAX = 1000;
541
class CVmStaticInitPage
542
{
543
public:
544
    CVmStaticInitPage(size_t cnt)
545
    {
546
        /* remember the number of records */
547
        cnt_ = cnt;
548
549
        /* no data yet */
550
        data_ = 0;
551
552
        /* not in a list yet */
553
        nxt_ = 0;
554
    }
555
556
    /* get an object/property ID given the index of a record */
557
    vm_obj_id_t get_obj_id(size_t idx)
558
        { return vmb_get_objid(get_rec(idx)); }
559
    vm_prop_id_t get_prop_id(size_t idx)
560
        { return vmb_get_propid(get_rec(idx) + VMB_OBJECT_ID); }
561
562
    /* get a raw record pointer given an index */
563
    const char *get_rec(size_t idx) { return data_ + (idx * 6); }
564
565
    /* next page in list */
566
    CVmStaticInitPage *nxt_;
567
568
    /* number of records in the page */
569
    size_t cnt_;
570
571
    /* 
572
     *   The data of the page.  Each record is six bytes long, in portable
573
     *   format: a UINT4 for the object ID, and a UINT2 for the property
574
     *   ID. 
575
     */
576
    const char *data_;
577
};
578
579
580
/* ------------------------------------------------------------------------ */
581
/*
582
 *   Image file resource loader interface.  This is an abstract class
583
 *   interface that must be provided to load_resource_file() to provide
584
 *   per-resource loading.  
585
 */
586
class CVmImageLoaderMres
587
{
588
public:
589
    virtual ~CVmImageLoaderMres() { }
590
591
    /* load a resource */
592
    virtual void add_resource(uint32 seek_ofs, uint32 siz,
593
                              const char *res_name, size_t res_name_len) = 0;
594
595
    /* load a resource link */
596
    virtual void add_resource(const char *fname, size_t fnamelen,
597
                              const char *res_name, size_t res_name_len) = 0;
598
};
599
600
601
/* ------------------------------------------------------------------------ */
602
/*
603
 *   Image file interface.  This is an abstract interface that provides
604
 *   access to the data in an image file independently of the location of
605
 *   the data.  
606
 */
607
class CVmImageFile
608
{
609
public:
610
    /* delete the loader */
611
    virtual ~CVmImageFile() { }
612
    
613
    /* 
614
     *   Copy data from the image file to the caller's buffer.  Reads from
615
     *   the current file position. 
616
     */
617
    virtual void copy_data(char *buf, size_t len) = 0;
618
619
    /* 
620
     *   Allocate memory for and load data from the image file.  Reads from
621
     *   the current file position.  Returns a pointer to the allocated data.
622
     *   
623
     *   'remaining_in_page' is the amount of space remaining in the current
624
     *   block being read from the image, including the space being read
625
     *   here; this can be used as an upper bound for a new allocation if
626
     *   the concrete subclass wishes to allocate blocks for suballocation.  
627
     */
628
    virtual const char *alloc_and_read(size_t len, uchar xor_mask,
629
                                       ulong remaining_in_page) = 0;
630
631
    /*
632
     *   Determine if memory read with alloc_and_read() is writable.
633
     *   Returns true if so, false if not.  If the memory that
634
     *   alloc_and_read() returns is a copy of the external file (rather
635
     *   than mapped to the original external data, as might be the case
636
     *   on a small machine without external storage, such as a palm-top
637
     *   computer), this should return true. 
638
     */
639
    virtual int allow_write_to_alloc() = 0;
640
641
    /* free memory previously allocated by alloc_and_read */
642
    virtual void free_mem(const char *mem) = 0;
643
644
    /* seek to a new file position, as an offset from the start of the file */
645
    virtual void seek(long pos) = 0;
646
647
    /* get the current seek position */
648
    virtual long get_seek() const = 0;
649
650
    /* skip the given number of bytes */
651
    virtual void skip_ahead(long len) = 0;
652
};
653
654
655
/*
656
 *   Implementation of the generic stream interface for an image file block.
657
 *   This will limit reading to the data in the block.  
658
 */
659
class CVmImageFileStream: public CVmStream
660
{
661
public:
662
    CVmImageFileStream(CVmImageFile *fp, size_t len)
663
    {
664
        /* 
665
         *   remember the underlying image file, and the amount of space in
666
         *   our data block 
667
         */
668
        fp_ = fp;
669
        len_ = len;
670
    }
671
672
    /* read bytes into a buffer */
673
    virtual void read_bytes(char *buf, size_t len);
674
675
    /* write bytes */
676
    virtual void write_bytes(const char *, size_t);
677
678
    /* get/set the seek position */
679
    virtual long get_seek_pos() const { return fp_->get_seek(); }
680
    virtual void set_seek_pos(long pos) { fp_->seek(pos); }
681
682
private:
683
    /* our underlying image file reader */
684
    CVmImageFile *fp_;
685
686
    /* remaining data length in the underlying block */
687
    size_t len_;
688
};
689
690
691
/* ------------------------------------------------------------------------ */
692
/*
693
 *   Image file interface - external disk file 
694
 */
695
class CVmImageFileExt: public CVmImageFile
696
{
697
public:
698
    /* delete the image file loader */
699
    ~CVmImageFileExt();
700
    
701
    /* initialize with an underlying file */
702
    CVmImageFileExt(class CVmFile *fp)
703
    {
704
        /* remember our file */
705
        fp_ = fp;
706
707
        /* we don't have any suballocation blocks yet */
708
        mem_head_ = mem_tail_ = 0;
709
    }
710
    
711
    /* 
712
     *   CVmImageFile interface implementation 
713
     */
714
715
    /* copy data to the caller's buffer */
716
    void copy_data(char *buf, size_t len);
717
718
    /* allocate memory for and read data */
719
    const char *alloc_and_read(size_t len, uchar xor_mask,
720
                               ulong remaining_in_page);
721
722
    /* allow writing to alloc_and_read blocks */
723
    virtual int allow_write_to_alloc() { return TRUE; }
724
725
    /* 
726
     *   Free memory previously allocated with alloc_and_read.  We don't
727
     *   need to do anything here; once we allocate and load a block, we
728
     *   keep it in memory until the entire load image is deleted, at
729
     *   which time we free all of the associated memory.  
730
     */
731
    void free_mem(const char *) { }
732
733
    /* seek to a new file position */
734
    void seek(long pos);
735
736
    /* get the current seek position */
737
    long get_seek() const;
738
739
    /* skip the given number of bytes */
740
    void skip_ahead(long len);
741
742
private:
743
    /* allocate memory for loading data */
744
    char *alloc_mem(size_t siz, ulong remaining_in_page);
745
    
746
    /* the underlying file */
747
    class CVmFile *fp_;
748
749
    /* 
750
     *   Memory block list.  We keep a set of memory blocks for loading
751
     *   data via the alloc_and_read() method.  Rather than allocating an
752
     *   individual "malloc" block for each alloc_and_read() call, we
753
     *   allocate a large block, and suballocate memory out of the large
754
     *   block.  Since we don't know exactly how much memory we'll need in
755
     *   advance, we take a guess at how large a block we need,
756
     *   suballocate from the block until we fill it up, then allocate
757
     *   another block and fill it up, then allocate another, and so on.
758
     *   We keep all of the blocks we allocate in a linked list.
759
     */
760
    class CVmImageFileExt_blk *mem_head_;
761
    class CVmImageFileExt_blk *mem_tail_;
762
};
763
764
/* 
765
 *   aggregate allocation block size - use a size that should be reasonably
766
 *   safe for 16-bit platforms (not over 64k, and a bit less to allow for
767
 *   some malloc overhead) 
768
 */
769
const size_t VMIMAGE_EXT_BLK_SIZE = 65000;
770
771
/*
772
 *   Memory tracking structure for external file reader.  For each large
773
 *   memory block we allocate (for suballocation), we allocate one of
774
 *   these structures.  
775
 */
776
class CVmImageFileExt_blk
777
{
778
public:
779
    /* create the block, allocating the given number of bytes for the block */
780
    CVmImageFileExt_blk(size_t siz);
781
782
    /* delete the block */
783
    ~CVmImageFileExt_blk();
784
    
785
    /* suballocate memory out of the current block */
786
    char *suballoc(size_t siz);
787
788
    /* next block in the list */
789
    CVmImageFileExt_blk *nxt_;
790
791
    /* previous block in the list */
792
    CVmImageFileExt_blk *prv_;
793
794
    /* pointer to the start of the block */
795
    char *block_ptr_;
796
797
    /* number of bytes remaining in the block for future suballocations */
798
    size_t rem_;
799
800
    /* pointer to next free byte of the block */
801
    char *free_ptr_;
802
};
803
804
805
/* ------------------------------------------------------------------------ */
806
/*
807
 *   Image file interface - memory-mapped implementation.  This
808
 *   implementation assumes that the file is loaded into memory in a
809
 *   contiguous chunk, which can be addressed linearly.  
810
 */
811
class CVmImageFileMem: public CVmImageFile
812
{
813
public:
814
    ~CVmImageFileMem() { }
815
    
816
    /* initialize with an underlying block of pre-loaded data */
817
    CVmImageFileMem(const char *mem, long len)
818
    {
819
        /* remember where our data are */
820
        mem_ = mem;
821
        len_ = len;
822
823
        /* start at the beginning of the data */
824
        pos_ = 0;
825
    }
826
827
    /* 
828
     *   CVmImageFile interface implementation 
829
     */
830
831
    /* copy data to the caller's buffer */
832
    void copy_data(char *buf, size_t len);
833
834
    /* allocate memory for and read data */
835
    const char *alloc_and_read(size_t len, uchar xor_mask,
836
                               ulong remaining_in_page);
837
838
    /* 
839
     *   do not allow writing to alloc_and_read blocks, since we map these
840
     *   blocks directly to the underlying in-memory data 
841
     */
842
    virtual int allow_write_to_alloc() { return FALSE; }
843
844
    /* 
845
     *   Free memory allocated by alloc_and_read.  Since our underlying
846
     *   file is entirely in memory to start with, we don't actually ever
847
     *   allocate any memory; hence, we don't actually need to free any
848
     *   memory here.
849
     */
850
    void free_mem(const char *) { }
851
852
    /* seek to a new file position */
853
    void seek(long pos) { pos_ = pos; }
854
855
    /* get the current seek position */
856
    long get_seek() const { return pos_; }
857
    
858
    /* skip the given number of bytes */
859
    void skip_ahead(long len) { pos_ += len; }
860
861
private:
862
    /* the underlying memory block */
863
    const char *mem_;
864
865
    /* size in bytes of the underlying memory block */
866
    ulong len_;
867
868
    /* current offset within the memory block */
869
    long pos_;
870
};
871
872
873
#endif /* VMIMAGE_H */
874