| | 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 ×tamp_[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 | |