/* restart.c Copyright (C) 1989-2007 Codemist Ltd */ /* * Code needed to start off Lisp when no initial heap image is available, * or to re-instate links between heap and C-coded core if there IS a * heap loaded. This code is run in a state that is in effect (in-package * "lisp"). */ /* * This code may be used and modified, and redistributed in binary * or source form, subject to the "CCL Public License", which should * accompany it. This license is a variant on the BSD license, and thus * permits use of code derived from this in either open and commercial * projects: but it does require that updates to this code be made * available back to the originators of the package. * Before merging other code in with this or linking this code * with other packages or libraries please check that the license terms * of the other material are compatible with those of this. */ /* Signature: 048024f0 19-Jan-2008 */ #include "headers.h" #ifdef WIN32 #include <windows.h> #else #include <dlfcn.h> #endif #ifdef HAVE_UNISTD_H #include <unistd.h> #endif #include <sys/stat.h> #include <sys/types.h> #ifdef HAVE_FWIN extern int showmathInitialised; #endif #ifndef S_IRUSR #ifdef __S_IRUSR #define S_IRUSR __S_IRUSR #endif #endif #ifndef S_IWUSR #ifdef __S_IWUSR #define S_IWUSR __S_IWUSR #endif #endif #ifndef S_IXUSR #ifdef __S_IXUSR #define S_IXUSR __S_IXUSR #endif #endif /* * jit */ #ifdef JIT #ifndef WIN32 #include <sys/mman.h> #endif #endif extern int load_count, load_limit; /* * machineid.c is a dynamically-created file that contains * (a) Identification of the type of object file used by this system. * In many cases this is the ELF magic code for the machine. * (b) Information about the command used to compile C code. * (c) Header files relating to the Lisp-to-C compilation process. */ #include "machineid.c" Lisp_Object address_sign; Lisp_Object C_nil; Lisp_Object *stackbase; Lisp_Object * volatile stacklimit; Lisp_Object *nilsegment; Lisp_Object *stacksegment; int32_t stack_segsize = 1; char *exit_charvec = NULL; #ifdef NILSEG_EXTERNS uint32_t byteflip; Lisp_Object codefringe; Lisp_Object volatile codelimit; Lisp_Object fringe; Lisp_Object volatile heaplimit; Lisp_Object volatile vheaplimit; Lisp_Object vfringe; int32_t nwork; int32_t exit_reason; int32_t exit_count; uint32_t gensym_ser, print_precision, miscflags; int32_t current_modulus, fastget_size, package_bits; Lisp_Object lisp_true, lambda, funarg, unset_var, opt_key, rest_key; Lisp_Object quote_symbol, function_symbol, comma_symbol, comma_at_symbol; Lisp_Object cons_symbol, eval_symbol, work_symbol, evalhook, applyhook; Lisp_Object macroexpand_hook, append_symbol, exit_tag; Lisp_Object exit_value, catch_tags; #ifdef COMMON Lisp_Object keyword_package; #endif Lisp_Object current_package; Lisp_Object startfn; #ifdef COMMON Lisp_Object all_packages, package_symbol, internal_symbol; Lisp_Object external_symbol, inherited_symbol; #endif Lisp_Object gensym_base, string_char_sym, boffo; #ifdef COMMON Lisp_Object key_key, allow_other_keys, aux_key; #endif Lisp_Object err_table; #ifdef COMMON Lisp_Object format_symbol; #endif Lisp_Object progn_symbol; #ifdef COMMON Lisp_Object expand_def_symbol, allow_key_key, declare_symbol, special_symbol; #endif Lisp_Object lisp_work_stream, charvec, raise_symbol, lower_symbol, echo_symbol; Lisp_Object codevec, litvec, supervisor, B_reg, savedef, comp_symbol; Lisp_Object compiler_symbol, faslvec, tracedfn, lisp_terminal_io; Lisp_Object lisp_standard_output, lisp_standard_input, lisp_error_output; Lisp_Object lisp_trace_output, lisp_debug_io, lisp_query_io; Lisp_Object prompt_thing, faslgensyms, prinl_symbol, emsg_star, redef_msg; Lisp_Object expr_symbol, fexpr_symbol, macro_symbol; Lisp_Object cl_symbols, active_stream, current_module; Lisp_Object features_symbol, lisp_package, sys_hash_table; Lisp_Object help_index, cfunarg, lex_words, get_counts, fastget_names; Lisp_Object input_libraries, output_library, current_file, break_function; Lisp_Object standard_output, standard_input, debug_io; Lisp_Object error_output, query_io, terminal_io, trace_output, fasl_stream; Lisp_Object native_code, native_symbol, traceprint_symbol, loadsource_symbol; Lisp_Object gchook; Lisp_Object hankaku_symbol; Lisp_Object workbase[51]; #endif Lisp_Object user_base_0, user_base_1, user_base_2, user_base_3, user_base_4; Lisp_Object user_base_5, user_base_6, user_base_7, user_base_8, user_base_9; Lisp_Object eq_hash_tables, equal_hash_tables; /* * On an Intel 80x86 (because I am almost forced to) and on other machines * (much more cheerfully, and for choice!) I will arrange my memory as * a number of pages. A general pool of these pages gets used * to satisfy requests for heap, vector heap and BPS space. * * Since this code was first written it has become silly to even consider * computers with 16-bit segmented addressing! It is still convenient to * allocate memory in chunks, although that does set an upper limit to the * size of any individual object: this may hurt if a user wants a big vector * and it does constrain the range of big-numbers supported by the * artithmetic. */ void **pages, **heap_pages, **vheap_pages, **bps_pages, **native_pages; void **new_heap_pages, **new_vheap_pages, **new_bps_pages, **new_native_pages; #ifdef CONSERVATIVE page_map_t *page_map; #endif /* * Used for allocating jit functions executable space */ #ifdef JIT void *jit_space, *jit_space_p; unsigned long jit_size; #endif int pages_count, heap_pages_count, vheap_pages_count, bps_pages_count, native_pages_count; int new_heap_pages_count, new_vheap_pages_count, new_bps_pages_count, new_native_pages_count; char program_name[64] = {0}; #ifndef COMMON #ifdef HAVE_FWIN char **loadable_packages = NULL, **switches = NULL; #endif #endif int native_code_tag; int native_pages_changed; int32_t native_fringe; int current_fp_rep; static int old_fp_rep; static CSLbool flip_needed; static int old_page_bits; /* * The next function is handed a page * of hard code that has just been loaded into memory and it must scan it * performing all relevant relocation. fringe give the offset within the * page that is the first byte not in use. The first 4 bytes of the page * are reserved for storing fringe from one run to the next. The exact * format of the rest must be sufficient to allow this code to scan * and correct the code, but thus far I have not defined it, and it will * anyway tend to need extension each time a new target architecture is * incorporated (to support the new and curious relocation modes tha the * new machine requires). */ static void relocate_native_code(unsigned char *p, int32_t n) { /* * One helpful observation here. In pretty well all other parts of CSL * there is a possibility that an image file created on one computer will * be reloaded on another and so all the code is ultra-careful to avoid * sensitivity to byte order etc etc issues. But here the native code that * is being loaded MUST have been created using the conventions of the * current computer (otherwise I should not be loading it and I will be * in huge trouble when I try to execute code from it). So direct and * simple access to data is legitimate. */ int32_t k = 8; term_printf("Native code page type %d size %d to be relocated\n", native_code_tag, n); while (k <= n) { unsigned char *block = p + k; int32_t len = car32(block); term_printf("Block of %d bytes found\n", len); if (len == 0) { term_printf("End of native page reached\n"); break; } relocate_native_function(block); k += len; } } void relocate_native_function(unsigned char *bps) { /* * Just for now I will not support native code on 64-bit machines. * This is just to save me some hassle re-working this relocation mess! */ unsigned char *r, *next; int32_t n; int code; if (SIXTY_FOUR_BIT) return; /* No native for 64-bit architectures yet */ /* * Each chunk of memory allocated by make-native will have its length (in * bytes) in its first 32-bit word. Next comes the offset of the * start of real code in the block. Just after that there will be a * hunk of relocation information. The code proper must not start until * after the relocation records. Relocation information is stored in the * following format as a sequence of bytes: * 0 end of relocation information. * 1 to 170/xx encode a value 0 to 169 * 171 to 255/xx/yy extra byte yy extends following offset xx, and * its top bit is used to extend opcode to range * 0 to 169. * The opcode now in the range 0 to 169 is interpreted as * 169 no operation * otherwise (0-12)*(0-12) as target*mode */ r = bps + 4; n = *r++; /* code start offset in LSB format */ n |= (*r++) << 8; n |= (*r++) << 16; n |= (*r++) << 24; next = bps + n; #define RELOC_END 0 while ((code = *r++) != RELOC_END) { int32_t off = *r++; unsigned char *target; /* * A native compiler will have to generate a sequence of bytes that adhere to * the contorted format used here. */ if (code <= 170) code--; else { int off1 = *r++; code = 2*(code-171) + (off1 >> 7); off = off | ((off1 & 0x7f) << 8); } next += off; /* address where next relocation is to be applied */ #define RELOC_NOP 169 /* * One might like to note that with a long offset the NOP opcode turns into * an opcode byte 0xff. And if it then has the longest possible offset one] * gets the 3-byte sequence 0xff/0xff/0xff. */ if (code == RELOC_NOP) continue; #define RELOC_0_ARGS 0 #define RELOC_1_ARGS 1 #define RELOC_2_ARGS 2 #define RELOC_3_ARGS 3 #define RELOC_DIRECT_ENTRY 4 #define RELOC_VAR 5 #define RELOC_SELF_1 6 #define RELOC_SELF_2 7 switch (code % 13) { default: term_printf("Illegal relocation byte %.2x\n", code); my_exit(EXIT_FAILURE); case RELOC_SELF_1: /* * base of current native code block with an 8-bit offset. */ target = bps + *r++; break; case RELOC_SELF_2: /* * base of current native code block with 15 or 23-bit offset. The first byte * is the low 8-bits of the offset. The next is the next 7 bits, with its * 0x80 bit selecting whether a third byte is needed (which it will hardly * ever be). */ off = *r++; off = off + (*r++ << 8); if (off & 0x8000) off = (off & 0x7fff) + (*r++ << 15); target = bps + off; break; case RELOC_0_ARGS: /* * The next few relocation modes provide access to the C entrypoints * associated with a medium number of Lisp functions. The tables and * offsets used are documented in file "eval4.c" and are as used with the * byte-code compiler. */ target = (unsigned char *)zero_arg_functions[*r++]; break; case RELOC_1_ARGS: target = (unsigned char *)one_arg_functions[*r++]; break; case RELOC_2_ARGS: target = (unsigned char *)two_arg_functions[*r++]; break; case RELOC_3_ARGS: target = (unsigned char *)three_arg_functions[*r++]; break; case RELOC_DIRECT_ENTRY: /* * There are some entrypoints into the CSL kernel that are not * called using the usual Lisp conventions but are at a lower-level. * A selection of these are visible via the table "useful_functions" * in file fns3.c. This table can be extended if a native-mode compiler * needs access to any other speciality. */ target = (unsigned char *)useful_functions[*r++]; break; case RELOC_VAR: /* * The function address_f_var (in fns3.c) returns the address of a Lisp * internal variable. See there for the numeric encoding used and what can * be accessed. */ target = (unsigned char *)address_of_var(*r++); break; } #define RELMODE_ABSOLUTE 0 #define RELMODE_RELATIVE 1 #define RELMODE_REL_PLUS_4 2 #define RELMODE_REL_MINUS_2 3 #define RELMODE_REL_MINUS_4 4 #define RELMODE_REL_OFFSET 5 #define RELMODE_SPARE1 6 #define RELMODE_SPARE2 7 switch (code/13) { default: term_printf("Illegal relocation byte %.2x\n", code); my_exit(EXIT_FAILURE); case RELMODE_ABSOLUTE: /* * relocate by pointing a 32-bit value directly at the absolute address * of the target. */ /* * In this general section of the code there are a bunch of cases where I * cast to intptr_t and after that to int32_t. Well at present this section * of code can only even possibly get executed if these two types are the * same width! But on a 64-bit machine I would need to take extra care * relocating references to 64-bit addresses. */ *(int32_t *)next = (int32_t)(intptr_t)target; break; case RELMODE_RELATIVE: /* * relocate by setting a 32-bit value of the offset from its own first * byte to the target. */ *(int32_t *)next = (int32_t)((intptr_t)target - (intptr_t)next); break; case RELMODE_REL_PLUS_4: /* * relocate by setting a 32-bit value of the offset from the start of the * word after it. */ *(int32_t *)next = (int32_t)((intptr_t)target - ((intptr_t)next + 4)); break; case RELMODE_REL_MINUS_2: /* * relocate by setting a 32-bit value of the offset from the address 2 bytes * before its start. This may be used on machines where the relative address * is computed based on the start of the instruction rather than the start of * the field within the instruction that contains the offset. */ *(int32_t *)next = (int32_t)((intptr_t)target - ((intptr_t)next - 2)); break; case RELMODE_REL_MINUS_4: /* * relocate by setting a 32-bit value of the offset from the address 4 bytes * before its start. This may be used on machines where the relative address * is computed based on the start of the instruction rather than the start of * the field within the instruction that contains the offset. */ *(int32_t *)next = (int32_t)((intptr_t)target - ((intptr_t)next - 4)); break; case RELMODE_REL_OFFSET: /* * relocate by setting a 32-bit value of the offset from some place * offset using an 8-bit signed value from the start of the address. The * offset represents the number of bytes after the start of the address * that is to be used in the calculation. Note that the special values * -4, -2, 0 and 4 need never be used here because there are special * relocation modes for those common cases. */ code = *r++; if (code & 0x80) code |= ~0xff; /* Sign extend */ *(int32_t *)next = (int32_t)((intptr_t)target - ((intptr_t)next + code)); break; } } } static int32_t fread_count; static unsigned char *fread_ptr; #define FREAD_BUFFER_SIZE ((CSL_PAGE_SIZE - 1) & ~0xfff) static unsigned char *pair_c, *char_stack; static unsigned short int *pair_prev; static void Cfread(char *p, int32_t n) { /* * The decompression process does not need hashed access to see if * character-pairs have been seen before, but it can need a stack to * unwind codes that have very lengthy expansions. */ int c1, k; unsigned int prev, c, next_code; int32_t count = fread_count; unsigned char *ptr = fread_ptr; if (n < compression_worth_while) { while (n > count) { memcpy(p, ptr, (size_t)count); p += count; n -= count; ptr = (unsigned char *)stack; count = Iread(ptr, FREAD_BUFFER_SIZE); } if (n != 0) { memcpy(p, ptr, (size_t)n); ptr += n; count -= n; } fread_count = count; fread_ptr = ptr; return; } next_code = 256; if (count == 0) { ptr = (unsigned char *)stack; count = Iread(ptr, FREAD_BUFFER_SIZE); } c = *ptr++; count--; if (count == 0) { ptr = (unsigned char *)stack; count = Iread(ptr, FREAD_BUFFER_SIZE); } c = (c << 8) | *ptr++; count--; prev = c >> 4; *p++ = (char)prev; /* The first character is not compressed */ n--; while (n > 0) { if (count == 0) { ptr = (unsigned char *)stack; count = Iread(ptr, FREAD_BUFFER_SIZE); } c = ((c & 0xf) << 8) | *ptr++; count--; /* * Decode the next 12 bit character */ c1 = c; k = 1; while (c1 >= 256) { char_stack[k++] = pair_c[c1]; if (pair_prev[c1] > CODESIZE || k >= CODESIZE) { term_printf("Bad decoded char %x -> %x, k=%d\n", c1, pair_prev[c1], k); my_exit(EXIT_FAILURE); } c1 = pair_prev[c1]; } /* * Write the decoded stuff into the output array. */ n -= k; *p++ = (char)c1; while (k != 1) { *p++ = char_stack[--k]; } /* * ... then build up the decoding tables ready for next time. Of course * the table building in this decoder MUST exactly match the behaviour of * the compression code above. */ if (next_code >= CODESIZE) next_code = 256; else { pair_prev[next_code] = (unsigned short int)prev; pair_c[next_code] = (unsigned char)c1; next_code++; } prev = c; if (n <= 0) break; /* * read the next 12 bit character. */ if (count == 0) { ptr = (unsigned char *)stack; count = Iread(ptr, FREAD_BUFFER_SIZE); } c = *ptr++; count--; if (count == 0) { ptr = (unsigned char *)stack; count = Iread(ptr, FREAD_BUFFER_SIZE); } c = (c << 8) | *ptr++; count--; /* * Decode it... */ c1 = c >> 4; k = 1; while (c1 >= 256) { char_stack[k++] = pair_c[c1]; if (pair_prev[c1] > CODESIZE || k >= CODESIZE) { term_printf("Bad decoded char %x -> %x, k=%d\n", c1, pair_prev[c1], k); my_exit(EXIT_FAILURE); } c1 = pair_prev[c1]; } /* * Write the decoded stuff into the output array. */ n -= k; *p++ = (char)c1; while (k != 1) { *p++ = char_stack[--k]; } /* * ... then build up the decoding tables ready for next time. Of course * the table building in this decoder MUST exactly match the behaviour of * the compression code above. */ if (next_code >= CODESIZE) next_code = 256; else { pair_prev[next_code] = (unsigned short int)prev; pair_c[next_code] = (unsigned char)c1; next_code++; } prev = c >> 4; } fread_count = count; fread_ptr = ptr; } #define flip_bytes(a) \ (flip_needed ? flip_32bits_fn(a) : (a)) static uint32_t flip_32bits_fn(uint32_t x) { uint32_t b0, b1, b2, b3; b0 = (x >> 24) & 0xffU; b1 = (x >> 8) & 0xff00U; b2 = (x << 8) & 0xff0000U; b3 = (x << 24) & 0xff000000U; return b0 | b1 | b2 | b3; } #ifdef HAVE_UINT64_T /* * If I need to correct items on a 64-bit machine I will need to be * careful that I use flip_byte() on 32-bit data and flip_long_bytes() on * 64-bit stuff. Well I guess that is obvious! */ #define flip_long_bytes(a) \ (flip_needed ? flip_64bits_fn(a) : (a)) static uint64_t flip_64bits_fn(uint64_t x) { uint64_t b0, b1, b2, b3, b4, b5, b6, b7; b0 = (x >> 56) & ((uint64_t)0xff); b1 = (x >> 40) & (((uint64_t)0xff)<<8); b2 = (x >> 24) & (((uint64_t)0xff)<<16); b3 = (x >> 8) & (((uint64_t)0xff)<<24); b4 = (x << 8) & (((uint64_t)0xff)<<32); b5 = (x << 24) & (((uint64_t)0xff)<<40); b6 = (x << 40) & (((uint64_t)0xff)<<48); b7 = (x << 56) & (((uint64_t)0xff)<<56); return b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7; } #endif #define flip_halfwords(a) \ (!SIXTY_FOUR_BIT && flip_needed ? flip_halfwords_fn(a) : (a)) static uint32_t flip_halfwords_fn(uint32_t x) { uint32_t b0, b1, b2, b3; b0 = (x >> 8) & 0xffU; b1 = (x << 8) & 0xff00U; b2 = (x >> 8) & 0xff0000U; b3 = (x << 8) & 0xff000000U; return b0 | b1 | b2 | b3; } void convert_fp_rep(void *p, int old_rep, int new_rep, int type) { uint32_t *f = (uint32_t *)p; if (old_rep == new_rep) return; /* * type == 0 for sfloat, 1 for single float, 2 for double and 3 for extended. * in CSL mode only case 2 can arise. If I ever implement "long floats" * (ie 80-bit values) I will need to re-visit this code. */ if (type >= 2 && ((old_rep ^ new_rep) & FP_WORD_ORDER)) { uint32_t w = f[0]; f[0] = f[1]; f[1] = w; } /* * Note that I flip the bytes in each word and ALSO flip the order of the * words to achieve a full 64-bit flip here. */ if ((old_rep ^ new_rep) & FP_BYTE_ORDER) { f[0] = flip_32bits_fn(f[0]); if (type >= 2) f[1] = flip_32bits_fn(f[1]); } return; } static void adjust(Lisp_Object *cp) /* * If p is a pointer to an object that has moved, adjust it. */ { Lisp_Object nil = C_nil, p = flip_bytes(*cp); if (p == SPID_NIL) *cp = nil; else if (is_cons(p)) { intptr_t h = (intptr_t)heap_pages[(p>>PAGE_BITS) & PAGE_MASK]; *cp = (Lisp_Object)((char *)quadword_align_up(h) + (p & OFFSET_MASK)); } else if (is_immed_or_cons(p)) { #ifdef COMMON if (is_sfloat(p)) { intptr_t w = flip_bytes(p); /* delicate here!! */ convert_fp_rep((void *)&w, old_fp_rep, current_fp_rep, 0); *cp = w; } #endif *cp = p; /* Immediate data here */ } else { intptr_t h = (intptr_t)vheap_pages[(p>>PAGE_BITS) & PAGE_MASK]; *cp = (Lisp_Object)((char *)doubleword_align_up(h) + (p & OFFSET_MASK)); } } static void adjust_consheap(void) { nil_as_base int32_t page_number; for (page_number = 0; page_number < heap_pages_count; page_number++) { void *page = heap_pages[page_number]; char *low = (char *)quadword_align_up((intptr_t)page); char *start = low + CSL_PAGE_SIZE; int32_t len = flip_bytes((uint32_t)car32(low)); char *fr; qcar(low) = len; fr = low + len; fringe = (Lisp_Object)fr; heaplimit = (Lisp_Object)(low + SPARE); while (fr < start) { adjust((Lisp_Object *)fr); fr += sizeof(Lisp_Object); } } } entry_point1 entries_table1[] = { /* * All values that can go in the function cells of symbols to stand for * special interpreter activity are kept here. In most cases where there * is an entrypoint there is a corresponding one that behaves just the * same except that it has tracing enabled. */ {0, "illegal"}, {undefined1, "undefined1"}, {autoload1, "autoload1"}, {interpreted1, "interpreted1"}, {traceinterpreted1, "traceinterpreted1"}, {double_interpreted1, "double_interpreted1"}, {funarged1, "funarged1"}, {tracefunarged1, "tracefunarged1"}, {double_funarged1, "double_funarged1"}, {bytecoded1, "bytecoded1"}, {tracebytecoded1, "tracebytecoded1"}, {double_bytecoded1, "double_bytecoded1"}, {byteopt1, "byteopt1"}, {tracebyteopt1, "tracebyteopt1"}, {double_byteopt1, "double_byteopt1"}, {hardopt1, "hardopt1"}, {tracehardopt1, "tracehardopt1"}, {double_hardopt1, "double_hardopt1"}, {byteoptrest1, "byteoptrest1"}, {tracebyteoptrest1, "tracebyteoptrest1"}, {double_byteoptrest1, "double_byteoptrest1"}, {hardoptrest1, "hardoptrest1"}, {tracehardoptrest1, "tracehardoptrest1"}, {double_hardoptrest1, "double_hardoptrest1"}, {too_few_2, "too_few_2"}, {wrong_no_0a, "wrong_no_0a"}, {wrong_no_3a, "wrong_no_3a"}, {wrong_no_na, "wrong_no_na"}, /* * The batch here relate to function re-work that discards unwanted * extra arguments. */ {f1_as_0, "1->0"}, {f1_as_1, "1->1"}, #ifdef JIT {jitcompileme1, "jitcompileme1"}, #endif #ifdef CJAVA {java1, "java1"}, #endif {NULL, "dummy"} }; #define entry_table_size1 (sizeof(entries_table1)/sizeof(entries_table1[0])) entry_point2 entries_table2[] = { {0, "illegal"}, {undefined2, "undefined2"}, {autoload2, "autoload2"}, {interpreted2, "interpreted2"}, {traceinterpreted2, "traceinterpreted2"}, {double_interpreted2, "double_interpreted2"}, {funarged2, "funarged2"}, {tracefunarged2, "tracefunarged2"}, {double_funarged2, "double_funarged2"}, {bytecoded2, "bytecoded2"}, {tracebytecoded2, "tracebytecoded2"}, {double_bytecoded2, "double_bytecoded2"}, {byteopt2, "byteopt2"}, {tracebyteopt2, "tracebyteopt2"}, {double_byteopt2, "double_byteopt2"}, {hardopt2, "hardopt2"}, {tracehardopt2, "tracehardopt2"}, {double_hardopt2, "double_hardopt2"}, {byteoptrest2, "byteoptrest2"}, {tracebyteoptrest2, "tracebyteoptrest2"}, {double_byteoptrest2, "double_byteoptrest2"}, {hardoptrest2, "hardoptrest2"}, {tracehardoptrest2, "tracehardoptrest2"}, {double_hardoptrest2, "double_hardoptrest2"}, {too_many_1, "too_many_1"}, {wrong_no_0b, "wrong_no_0b"}, {wrong_no_3b, "wrong_no_3b"}, {wrong_no_nb, "wrong_no_nb"}, /* * The batch here relate to function re-work that discards unwanted * extra arguments. */ {f2_as_0, "2->0"}, {f2_as_1, "2->1"}, {f2_as_2, "2->2"}, #ifdef JIT {jitcompileme2, "jitcompileme2"}, #endif #ifdef CJAVA {java2, "java2"}, #endif {NULL, "dummy"} }; #define entry_table_size2 (sizeof(entries_table2)/sizeof(entries_table2[0])) entry_pointn entries_tablen[] = { {0, "illegal"}, {undefinedn, "undefinedn"}, {autoloadn, "autoloadn"}, {interpretedn, "interpretedn"}, {traceinterpretedn, "traceinterpretedn"}, {double_interpretedn, "double_interpretedn"}, {funargedn, "funargedn"}, {tracefunargedn, "tracefunargedn"}, {double_funargedn, "double_funargedn"}, {bytecoded0, "bytecoded0"}, {tracebytecoded0, "tracebytecoded0"}, {double_bytecoded0, "double_bytecoded0"}, {bytecoded3, "bytecoded3"}, {tracebytecoded3, "tracebytecoded3"}, {double_bytecoded3, "double_bytecoded3"}, {bytecodedn, "bytecodedn"}, {tracebytecodedn, "tracebytecodedn"}, {double_bytecodedn, "double_bytecodedn"}, {byteoptn, "byteoptn"}, {tracebyteoptn, "tracebyteoptn"}, {double_byteoptn, "double_byteoptn"}, {hardoptn, "hardoptn"}, {tracehardoptn, "tracehardoptn"}, {double_hardoptn, "double_hardoptn"}, {byteoptrestn, "byteoptrestn"}, {tracebyteoptrestn, "tracebyteoptrestn"}, {double_byteoptrestn, "double_byteoptrestn"}, {hardoptrestn, "hardoptrestn"}, {tracehardoptrestn, "tracehardoptrestn"}, {double_hardoptrestn, "double_hardoptrestn"}, {wrong_no_1, "wrong_no_1"}, {wrong_no_2, "wrong_no_2"}, /* * The batch here relate to function variants that discard unwanted * extra arguments and call something else. */ {f0_as_0, "0->0"}, {f3_as_0, "3->0"}, {f3_as_1, "3->1"}, {f3_as_2, "3->2"}, {f3_as_3, "3->3"}, #ifdef JIT {jitcompileme0, "jitcompileme0"}, {jitcompileme3, "jitcompileme3"}, {jitcompilemen, "jitcompilemen"}, #endif #ifdef CJAVA {java0, "java0"}, {java3, "java3"}, {javan, "javan"}, #endif {NULL, "dummy"} }; #define entry_table_sizen (sizeof(entries_tablen)/sizeof(entries_tablen[0])) entry_pointn entries_tableio[] = { {0, "illegal"}, {(void *)char_from_illegal, "char_from_illegal"}, {(void *)char_to_illegal, "char_to_illegal"}, {(void *)read_action_illegal, "read_action_illegal"}, {(void *)write_action_illegal, "write_action_illegal"}, {(void *)char_from_terminal, "char_from_terminal"}, {(void *)char_to_terminal, "char_to_terminal"}, {(void *)read_action_terminal, "read_action_terminal"}, {(void *)write_action_terminal, "write_action_terminal"}, {(void *)char_from_file, "char_from_file"}, {(void *)char_to_file, "char_to_file"}, {(void *)read_action_file, "read_action_file"}, {(void *)read_action_output_file, "read_action_output_file"}, {(void *)write_action_file, "write_action_file"}, {(void *)binary_outchar, "binary_outchar"}, {(void *)char_from_list, "char_from_list"}, {(void *)char_to_list, "char_to_list"}, {(void *)code_to_list, "code_to_list"}, {(void *)read_action_list, "read_action_list"}, {(void *)write_action_list, "write_action_list"}, {(void *)count_character, "count_character"}, {(void *)char_to_pipeout, "char_to_pipeout"}, {(void *)write_action_pipe, "write_action_pipe"}, {(void *)char_from_synonym, "char_from_synonym"}, {(void *)char_to_synonym, "char_to_synonym"}, {(void *)read_action_synonym, "read_action_synonym"}, {(void *)write_action_synonym, "write_action_synonym"}, {(void *)char_from_concatenated, "char_from_concatenated"}, {(void *)char_to_broadcast, "char_to_broadcast"}, {(void *)read_action_concatenated, "read_action_concatenated"}, {(void *)write_action_broadcast, "write_action_broadcast"}, {(void *)char_from_echo, "char_from_echo"}, {NULL, "dummy"} }; #define entry_table_sizeio (sizeof(entries_tableio)/sizeof(entries_tableio[0])) static struct entry_lookup1 { int32_t code; one_args *entry; char *s; } entry_lookup1[entry_table_size1]; static struct entry_lookup2 { int32_t code; two_args *entry; char *s; } entry_lookup2[entry_table_size2]; static struct entry_lookupn { int32_t code; n_args *entry; char *s; } entry_lookupn[entry_table_sizen]; static int MS_CDECL order_lookup_entries(void const *aa, void const *bb) { /* * I rely here on having entry_lookup[1,2,n] all the same shape so that * when I want to sort I only use one comparison function. */ struct entry_lookup1 *a = (struct entry_lookup1 *)aa, *b = (struct entry_lookup1 *)bb; intptr_t ap = (intptr_t)a->entry, bp = (intptr_t)b->entry; if (ap < bp) return -1; else if (ap > bp) return 1; else return 0; } void set_up_entry_lookup(void) /* * This makes a sorted version of entries_table. Since the table is * only a few dozen words long it hardly seems worth being too clever, * but the C library provides qsort() for me so I use it. */ { int i; for (i=0; i<entry_table_size1; i++) { entry_lookup1[i].code = i; entry_lookup1[i].entry = entries_table1[i].p; entry_lookup1[i].s = entries_table1[i].s; } qsort((void *)entry_lookup1, entry_table_size1, sizeof(struct entry_lookup1), order_lookup_entries); for (i=0; i<entry_table_size2; i++) { entry_lookup2[i].code = i; entry_lookup2[i].entry = entries_table2[i].p; entry_lookup2[i].s = entries_table2[i].s; } qsort((void *)entry_lookup2, entry_table_size2, sizeof(struct entry_lookup2), order_lookup_entries); for (i=0; i<entry_table_sizen; i++) { entry_lookupn[i].code = i; entry_lookupn[i].entry = entries_tablen[i].p; entry_lookupn[i].s = entries_tablen[i].s; } qsort((void *)entry_lookupn, entry_table_sizen, sizeof(struct entry_lookupn), order_lookup_entries); } int32_t code_up_fn1(one_args *e) { int low = 0, high = entry_table_size1-1; while (low < high) { int mid = (high + low)/2; intptr_t s = (intptr_t)entry_lookup1[mid].entry; if (s == (intptr_t)e) return entry_lookup1[mid].code; if ((intptr_t)s < (intptr_t)e) low = mid + 1; else high = mid - 1; } if (low == high && entry_lookup1[low].entry == e) return entry_lookup1[low].code; else return 0; } int32_t code_up_fn2(two_args *e) { int low = 0, high = entry_table_size2-1; while (low < high) { int mid = (high + low)/2; intptr_t s = (intptr_t)entry_lookup2[mid].entry; if (s == (intptr_t)e) return entry_lookup2[mid].code; if ((intptr_t)s < (intptr_t)e) low = mid + 1; else high = mid - 1; } if (low == high && entry_lookup2[low].entry == e) return entry_lookup2[low].code; else return 0; } int32_t code_up_fnn(n_args *e) { int low = 0, high = entry_table_sizen-1; while (low < high) { int mid = (high + low)/2; intptr_t s = (intptr_t)entry_lookupn[mid].entry; if (s == (intptr_t)e) return entry_lookupn[mid].code; if ((intptr_t)s < (intptr_t)e) low = mid + 1; else high = mid - 1; } if (low == high && entry_lookupn[low].entry == e) return entry_lookupn[low].code; else return 0; } int32_t code_up_io(void *e) { int i; for (i=0; i<entry_table_sizen; i++) { if (entries_tableio[i].p == e) return i; } return 0; } static void adjust_vecheap(void) { nil_as_base int32_t page_number, i; intptr_t iw; for (page_number = 0; page_number < vheap_pages_count; page_number++) { void *page = vheap_pages[page_number]; char *low = (char *)doubleword_align_up((intptr_t)page); int32_t len = flip_bytes((uint32_t)car32(low)); char *fr; qcar(low) = len; fr = low + len; vfringe = (Lisp_Object)fr; vheaplimit = (Lisp_Object)(low + (CSL_PAGE_SIZE - 8)); low += 8; while (low < fr) { Header h = flip_bytes(*(Header *)low); *(Header *)low = h; if (is_symbol_header(h)) { Lisp_Object ss = (Lisp_Object)(low + TAG_SYMBOL); adjust(&qvalue(ss)); adjust(&qenv(ss)); adjust(&qpname(ss)); adjust(&qplist(ss)); adjust(&qfastgets(ss)); #ifdef COMMON adjust(&qpackage(ss)); #endif /* * The mess here is because when CSL is re-loaded the position of all * C-coded entrypoints will very probably have changed since the * previous run - the set of entrypoints tested for here has to be * a complete list, except for ones established via "restart.c". Note * that setup establishes entrypoints later on, so I can afford to leave * junk in the function cells of things that will be initialised then. * Thus if a "real" function pointer left over from last time happens * to look like one of the small integers used here to stand for special * built-in cases the false-hit I get here is not important. * Also note that at present for 64-bit systems flip_bytes is a no-op * so image files are not portable across byte-order in that case. */ iw = flip_bytes(ifn1(ss)); if (0 < iw && iw < entry_table_size1) ifn1(ss) = (intptr_t)entries_table1[iw].p; else ifn1(ss) = (intptr_t)undefined1; iw = flip_bytes(ifn2(ss)); if (0 < iw && iw < entry_table_size2) ifn2(ss) = (intptr_t)entries_table2[iw].p; else ifn2(ss) = (intptr_t)undefined2; iw = flip_bytes(ifnn(ss)); if (0 < iw && iw < entry_table_sizen) ifnn(ss) = (intptr_t)entries_tablen[iw].p; else ifnn(ss) = (intptr_t)undefinedn; qcount(ss) = flip_bytes(qcount(ss)); low += symhdr_length; continue; } else switch (type_of_header(h)) { #ifdef COMMON case TYPE_RATNUM: case TYPE_COMPLEX_NUM: adjust((Lisp_Object *)(low+CELL)); adjust((Lisp_Object *)(low+2*CELL)); break; #endif case TYPE_HASH: case TYPE_SIMPLE_VEC: case TYPE_ARRAY: case TYPE_STRUCTURE: for (i=CELL; i<doubleword_align_up(length_of_header(h)); i+=CELL) adjust((Lisp_Object *)(low+i)); break; case TYPE_MIXED1: case TYPE_MIXED2: case TYPE_MIXED3: case TYPE_STREAM: for (i=CELL; i<4*CELL; i+=CELL) adjust((Lisp_Object *)(low+i)); if (!SIXTY_FOUR_BIT) { for (; i<doubleword_align_up(length_of_header(h)); i+=4) *(uint32_t *)(low+i) = flip_bytes(*(uint32_t *)(low+i)); } if (type_of_header(h) == TYPE_STREAM) { Lisp_Object ss = (Lisp_Object)(low + TAG_VECTOR); iw = elt(ss, 4); if (0 < iw && iw < entry_table_sizeio) elt(ss, 4) = (intptr_t)entries_tableio[iw].p; else elt(ss, 4) = (intptr_t)char_to_illegal; iw = elt(ss, 5); if (0 < iw && iw < entry_table_sizeio) elt(ss, 5) = (intptr_t)entries_tableio[iw].p; else elt(ss, 5) = (intptr_t)write_action_illegal; iw = elt(ss, 8); if (0 < iw && iw < entry_table_sizeio) elt(ss, 8) = (intptr_t)entries_tableio[iw].p; else elt(ss, 8) = (intptr_t)char_from_illegal; iw = elt(ss, 9); if (0 < iw && iw < entry_table_sizeio) elt(ss, 9) = (intptr_t)entries_tableio[iw].p; else elt(ss, 9) = (intptr_t)read_action_illegal; } break; case TYPE_BIGNUM: case TYPE_VEC32: if (!SIXTY_FOUR_BIT) { for (i=CELL; i<doubleword_align_up(length_of_header(h)); i+=4) *(uint32_t *)(low+i) = flip_bytes(*(uint32_t *)(low+i)); } break; case TYPE_VEC16: if (!SIXTY_FOUR_BIT) { for (i=CELL; i<doubleword_align_up(length_of_header(h)); i+=4) *(uint32_t *)(low+i) = flip_halfwords(*(uint32_t *)(low+i)); } break; case TYPE_DOUBLE_FLOAT: /* * note that this conversion is triggered by the vector header, not by * the pointer to the object, so punning associated with the pnames of * un-printed gensyms will not cause any confusion. */ convert_fp_rep((void *)(low + 8), old_fp_rep, current_fp_rep, 2); break; #ifdef COMMON case TYPE_SINGLE_FLOAT: convert_fp_rep((void *)(low + CELL), old_fp_rep, current_fp_rep, 1); break; case TYPE_LONG_FLOAT: /* Beware - if long floats move up to 3-word values the +8 here will change */ convert_fp_rep((void *)(low + 8), old_fp_rep, current_fp_rep, 3); break; #endif case TYPE_FLOAT32: for (i=CELL; i<doubleword_align_up(length_of_header(h)); i+=4) convert_fp_rep((void *)(low+i), old_fp_rep, current_fp_rep, 1); break; case TYPE_FLOAT64: for (i=8; i<doubleword_align_up(length_of_header(h)); i+=8) convert_fp_rep((void *)(low+i), old_fp_rep, current_fp_rep, 2); break; default: break; } low += doubleword_align_up(length_of_header(h)); } } } static void adjust_bpsheap(void) /* * This is needed so that (e.g.) headers in the code here get byte-flipped * if necessary. Also to set codefringe. */ { nil_as_base int32_t page_number; #ifdef ENVIRONMENT_VECTORS_IN_BPS_HEAP int32_t i; #endif codelimit = codefringe = 0; for (page_number = 0; page_number < bps_pages_count; page_number++) { void *page = bps_pages[page_number]; char *low = (char *)doubleword_align_up((intptr_t)page); int32_t len = flip_bytes((uint32_t)car32(low)); char *fr; qcar(low) = len; fr = low + len; codefringe = (Lisp_Object)fr; codelimit = (Lisp_Object)(low + 8); while (fr < low + CSL_PAGE_SIZE) { Header h = flip_bytes(*(Header *)fr); *(Header *)fr = h; #ifdef ENVIRONMENT_VECTORS_IN_BPS_HEAP switch (type_of_header(h)) { case TYPE_SIMPLE_VEC: /* This option not used at present */ for (i=CELL; i<doubleword_align_up(length_of_header(h)); i+=CELL) adjust((Lisp_Object *)(fr+i)); break; default: break; } #endif fr += doubleword_align_up(length_of_header(h)); } } } void adjust_all(void) { int32_t i; Lisp_Object nil = C_nil; qheader(nil) = TAG_ODDS+TYPE_SYMBOL+SYM_SPECIAL_VAR; #ifdef COMMON qheader(nil) |= SYM_EXTERN_IN_HOME; #endif qvalue(nil) = nil; qenv(nil) = nil; ifn1(nil) = (intptr_t)undefined1; ifn2(nil) = (intptr_t)undefined2; ifnn(nil) = (intptr_t)undefinedn; adjust(&(qpname(nil))); /* not a gensym */ adjust(&(qplist(nil))); adjust(&(qfastgets(nil))); #ifdef COMMON adjust(&(qpackage(nil))); #endif copy_into_nilseg(NO); for (i = first_nil_offset; i<last_nil_offset; i++) adjust(&BASE[i]); copy_out_of_nilseg(NO); adjust_consheap(); adjust_vecheap(); adjust_bpsheap(); } static void *allocate_page(void) { if (pages_count == 0) fatal_error(err_no_store); return pages[--pages_count]; } #ifdef MEMORY_TRACE #ifndef CHECK_ONLY intptr_t memory_base, memory_size, memory_count, memory_records = 0; unsigned char *memory_map = NULL; static intptr_t memory_lowest = 0x7fffffff, memory_highest = -1; FILE *memory_file = NULL; void memory_comment(int n) { if (memory_map != NULL) { putc(0xc0 + (n & 0x3f), memory_file); putc(0, memory_file); putc(0, memory_file); } } int kk = 0; static void identify_one(void *p, intptr_t size, int type) { int32_t i, j; intptr_t base = (intptr_t)p; int32_t a = 0, b = 0; intptr_t da = 1, db = 1; intptr_t click = size/0x400; switch (type) { case 0: b = click; break; case 1: db = -1; break; case 2: b = click; da = db = 2; break; case 3: da = 2; db = -2; break; case 4: db = 0; break; case 5: da = -1; db = 0; break; default: b = click; da = db = 0; break; } if (size > 256) { da *= (size/256); db *= (size/256); } memory_count |= 0x3ff; cmemory_reference(base); memory_comment(kk ? 3 : 5); kk = !kk; for (i=0; i<32; i++) { int x; memory_count |= 0x3ff; cmemory_reference(base); for (j=0; j<0x400; j++) { x = a + j*(size/8); while (x > size) x -= size; while (x < 0) x += size; cmemory_reference(base+x); x = b + j*(size/8); while (x > size) x -= size; while (x < 0) x += size; cmemory_reference(base+x); } a += da; b += db; } } static void identify_page(void *p[], int32_t n, int type) { while (n != 0) { void *w = p[--n]; if (w != NULL) identify_one(w, CSL_PAGE_SIZE, type); } } void identify_page_types() { identify_page(pages, pages_count, 0); identify_page(heap_pages, heap_pages_count, 1); identify_page(vheap_pages, vheap_pages_count, 2); identify_page(bps_pages, bps_pages_count, 3); identify_page(native_pages, native_pages_count, 4); identify_one((void *)stacksegment, CSL_PAGE_SIZE, 5); identify_one((void *)nilsegment, NIL_SEGMENT_SIZE, 6); } #endif /* CHECK_ONLY */ long int car_counter; unsigned long int car_low, car_high; Cons_Cell *memory_reference(intptr_t p) { if (p & 0x7) { term_printf("Access to mis-aligned address %.8x\n", (int)p); ensure_screen(); abort(); } return (Cons_Cell *)cmemory_reference(p); } char *cmemory_reference(intptr_t p) { #ifdef CHECK_ONLY return (char *)p; #else intptr_t a = p - memory_base; if (memory_map != NULL && a >= 0 && a < memory_size) { int bit; a = a >> 2; /* Get a word address */ a = a >> 2; /* reduce to 4-word resolution */ if (memory_count >= car_counter && (unsigned long int)a >= car_low && (unsigned long int)a <= car_high) { Lisp_Object nil = C_nil; if (exception_pending()) nil = (Lisp_Object)((intptr_t)nil ^ 1); interrupt_pending = 1; miscflags |= HEADLINE_FLAG | MESSAGES_FLAG; car_counter = 0x7fffffff; /* Do not interrupt again */ } bit = 1 << (a & 7); a = a >> 3; if (a < memory_lowest) memory_lowest = a; if (a > memory_highest) memory_highest = a; memory_map[a] |= bit; if ((++memory_count & 0x3ff) == 0) /* Every 1024 references... */ { unsigned char *pp; int c; int32_t run = 0, i; /* * I use a run-length encoded representation for the file that I write out. * Each scan-line is stored as a collection of bytes each of which indicates * the number of '0' items before the next '1' in the bit-vector. The encoding * of individual lengths is as follows: * 0 - 127 1 byte * 128 - 16K First byte has 0x80 plus 6 bits of data (+ 1 more) * 16K - 4M First byte has 0xc0 plus 6 bits of data (+ 2 more) * The byte pair (0x8n, 0x00) stands for n times 4M as a a prefix to * one of the above. This gives up to 2^28 as the max span. * The byte pair (0x80, 0x00) can be used to terminate a line. * Codes (0xcn, 0x00, 0x00) give 64 special codes that can be used * to interveave comments and annotations within the stream. */ pp = memory_map + memory_lowest; run = 8*memory_lowest; for (i=memory_lowest; i<=memory_highest; i++) { c = *pp++; if (c != 0) { bit = 1; while ((c & bit) == 0) run++, bit = bit << 1; if (run >= 0x400000) { putc(0x80 + ((run >> 22) & 0x3f), memory_file); putc(0x00, memory_file); run &= 0x3fffff; } if (run < 0x80) putc(run, memory_file); else if (run < 0x4000) { putc(0x80 + (run & 0x3f), memory_file); putc((run >> 6) & 0xff, memory_file); } else { putc(0xc0 + (run & 0x3f), memory_file); putc((run >> 6) & 0xff, memory_file); putc((run >> 14) & 0xff, memory_file); } c &= ~bit; run = 0; bit = bit << 1; while (c != 0) { while ((c & bit) == 0) run++, bit = bit << 1; putc(run, memory_file); c &= ~bit; run = 0; bit = bit << 1; } while (bit != 0x100) run++, bit = bit << 1; } else run += 8; } putc(0x80, memory_file); putc(0x00, memory_file); memory_lowest = 0x7fffffff; memory_highest = -1; memset(memory_map, 0, memory_size/32+8); memory_records++; } } return (char *)p; #endif /* CHECK_ONLY */ } #endif static char *global_handle; void *my_malloc(size_t n) { #ifdef NO_WORRY_ABOUT_MEMORY_PROBLEMS return (*malloc_hook)(n); #else /* * The idea here is INTENDED to provide a small amount of extra checking and * robustness about use of malloc and free. It is very probable these days * that I would do MUCH better to use a well-developed separate package * to help me out here - eg I understand that "valgrind" is useful for * detecting memory leaks... */ #define EXPLICIT_FREE_AT_END_OF_RUN 1 char *r = (char *)(*malloc_hook)(n+64); int32_t *p = (int32_t *)quadword_align_up(r); /* * | ... | : | | | | | | | to user | | | * r p <-r-> n 55aa 1234 3456 1234 3456 8765 cba9 * where p is quadword aligned whatever r is. * */ if (r == NULL) return NULL; n = quadword_align_up(n); inject_randomness((int)(intptr_t)r); if (!SIXTY_FOUR_BIT) p[1] = 0; ((void **)p)[0] = r; /* base address for free() */ p[2] = n; /* only permit 32-bit size */ p[3] = 0x5555aaaa; p[4] = 0x12345678; /* Marker words for security */ p[5] = 0x3456789a; p[6] = 0x12345678; p[7] = 0x3456789a; r = (char *)&p[8]; car32(r+n) = 0x87654321; car32(r+n+4) = 0xcba98765; return (void *)r; #endif } static char *big_chunk_start, *big_chunk_end; #ifdef EXPLICIT_FREE_AT_END_OF_RUN static void my_free(void *r) { #ifdef NO_WORRY_ABOUT_MEMORY_PROBLEMS char *rr = (char *)r; /* * I will not free it if the pointer is strictly inside the single big * chunk that I grabbed at the start of the run. */ if (rr > big_chunk_start && rr <= big_chunk_end) return; int32_t *p, *q, n; *(free_hook)(r); #else /* NO_WORRY... */ int32_t *p, *q, n; char *rr = (char *)r; /* * I will not free it if the pointer is strictly inside the single big * chunk that I grabbed at the start of the run. */ if (rr > big_chunk_start && rr <= big_chunk_end) return; p = (int32_t *)r - 8; n = p[2]; if (p[4] != 0x12345678 || p[5] != 0x3456789a) { term_printf("Corruption at start of memory block %p: %.8x %.8x\n", r, p[4], p[5]); ensure_screen(); my_exit(0); } q = (int32_t *)((char *)r + n); if (q[0] != 0x87654321 || q[1] != 0xcba98765) { term_printf("Corruption at end of memory block %p: %.8x %.8x\n", r, q[0], q[1]); ensure_screen(); my_exit(0); } (*free_hook)((void *)((void **)p)[0]); #endif } #endif static void *my_malloc_1(size_t n) /* * This is a pretty silly function - it gobbles up 24Kbytes of * stack and then just calls malloc - it stuffs a pointer to the * stack-chunk into a static variable so that compilers can not * detect (I hope!) that the array remains unused. The purpose of this * is to make malloc fail if it is about to encroach on space that * should be used for stack. This is relevant on small systems where * stack and heap grow towards one another and where one space has been * grabbed by malloc it is unavailable for stack (even if it is FREEd). * The number 24000 is pretty arbitrary - but if I have 24K bytes of stack * I will be able to do at least something. * Also this code verifies that the memory addresses returned have the * correct most significant bit. I allocate just a bit more memory than * is really needed to leave a one-word (or so) guard-band between * allocated blocks. This is necessary on some releases of an SGI C * compiler (library) where blocks of memory that are word but not * doubleword aligned can be returned. */ { char gobble_stack[24000]; char *r; intptr_t pun, pun1; global_handle = gobble_stack; r = (char *)my_malloc(n+16); pun = (intptr_t)r; pun1 = (intptr_t)(r + n); /* * I will moan if the block of memory allocated spans zero. * Note that if this does happen then something very funny is happening * about 0 cast to a pointer (i.e. a NULL pointer) since NULL is supposed * not to be valid as an address (?) but appears to be within the address * range of the block of store just allocated. */ if ((pun ^ pun1) < 0) fatal_error(err_mem_spans_zero); /* * Now if I get a block with the "wrong" top bit I will just return NULL * to suggest that no more memory was available - CSL can then proceed * or fail as it sees fit. */ /* * For dynamic address sign I should not test the address sign on the * first call - instead I just remember what it was. On subsequent calls * I will check it. */ if (nilsegment != NULL) { if ((pun + address_sign) < 0) return NULL; /* fatal_error(err_top_bit); */ } else address_sign = pun & GC_BIT_P; return (void *)r; } static void *my_malloc_2(size_t n) /* * Rather like my_malloc_1(), but does NOT check the sign bit of the * returned pointer. Provided as a place to put hooks to check memory * allocation problems. */ { char gobble_stack[24000]; char *r; global_handle = gobble_stack; r = (char *)my_malloc(n+16); return (void *)r; } static void init_heap_segments(double store_size) /* * This function just makes nil and the pool of page-frames available */ { char *memfile = "memory.use"; /* For memory statistics etc */ pages = (void **)my_malloc_2(MAX_PAGES*sizeof(void *)); #ifdef CONSERVATIVE page_map = (page_map_t *)my_malloc_2(MAX_PAGES*sizeof(page_map_t)); #endif heap_pages = (void **)my_malloc_2(MAX_PAGES*sizeof(void *)); vheap_pages = (void **)my_malloc_2(MAX_PAGES*sizeof(void *)); bps_pages = (void **)my_malloc_2(MAX_BPS_PAGES*sizeof(void *)); native_pages = (void **)my_malloc_2(MAX_NATIVE_PAGES*sizeof(void *)); new_heap_pages = (void **)my_malloc_2(MAX_PAGES*sizeof(void *)); new_vheap_pages = (void **)my_malloc_2(MAX_PAGES*sizeof(void *)); new_bps_pages = (void **)my_malloc_2(MAX_BPS_PAGES*sizeof(void *)); new_native_pages = (void **)my_malloc_2(MAX_NATIVE_PAGES*sizeof(void *)); pair_c = (unsigned char *)my_malloc_2(CODESIZE); /* * Sets up codebuffer for jit functions */ #ifdef JIT jit_size = JIT_INIT_SIZE; #ifdef WIN32 DWORD old_protection_status; printf("About to VirtualAlloc\n"); fflush(stdout); jit_space = VirtualAlloc( NULL, /* system selects address */ jit_size, /* size to allocate */ MEM_RESERVE | MEM_COMMIT,/* allocate reserved pages */ PAGE_EXECUTE_READWRITE); /* Execute, Read and Write access */ printf("VirtualAlloc = %p\n", jit_space); fflush(stdout); /* * Now just to show that I know how to I will change the protection of * the dynamic page to "read-only" so that nobody else can clobber it by * accident (or in malice). */ /* VirtualProtect( shell, 8192, PAGE_READONLY, &old_protection_status); */ #else jit_space = mmap(NULL, jit_size, PROT_WRITE|PROT_EXEC,MAP_PRIVATE|MAP_ANONYMOUS, 0,0); if (jit_space==(caddr_t)-1) { perror("mmap failed"); } #endif jit_space_p = jit_space; #endif /* * The next line is utterly unsatisfactory at present */ char_stack = (unsigned char *)my_malloc_2(CSL_PAGE_SIZE+16 /*CODESIZE*/); pair_prev = (unsigned short int *) my_malloc_2(CODESIZE*sizeof(unsigned short int)); if (pages == NULL || #ifdef CONSERVATIVE page_map == NULL || #endif new_heap_pages == NULL || new_vheap_pages == NULL || new_bps_pages == NULL || new_native_pages == NULL || heap_pages == NULL || vheap_pages == NULL || bps_pages == NULL || native_pages == NULL || pair_c == NULL || char_stack == NULL || pair_prev == NULL) { fatal_error(err_no_store); } { /* * Using an int32_t here is about to get embarassing as I move to 64-bit * machines and the amount of memory I ought to use grows to be over * 2 or over 4 Gbytes... */ int32_t free_space = SIXTY_FOUR_BIT ? 128000000 : 32000000; int32_t request; /* * There are two special cases where I will override the default, both * of which relate to "trick" builds for small machines. The two cases I * have most recently used these were * (a) Building for an HP Ipaq 4700 PDA * (b) Building to run on a Linksys router (!) */ #if defined UNDER_CE || PAGE_BITS == 18 free_space = 16000000; #endif request = (int32_t)store_size; if (request != 0) free_space = 1024*request; free_space = free_space/(CSL_PAGE_SIZE+4); if (free_space > MAX_PAGES) free_space = MAX_PAGES; pages_count = heap_pages_count = vheap_pages_count = bps_pages_count = native_pages_count = 0; native_fringe = 0; /* * I grab memory using a function called my_malloc_1(), which verifies that * all addresses used in the heap have the same top bit. The very first time * it is called nilsegment will be NULL - that time it does less checking. */ nilsegment = NULL; { size_t n = (size_t)(NIL_SEGMENT_SIZE+free_space*(CSL_PAGE_SIZE+16)); /* * I try to get the whole of the initial hunk of memory that I need in * one gulp since that (maybe) gives me the best chance to obtain all * the memory in just one half of my address space. */ char *pool = (char *)my_malloc_1(n); /* * I get 8 bytes more than seems necessary because I will need to * align my page frames up to a doubleword boundary, and that can * potentially waste 7 bytes. */ if (pool != NULL) { big_chunk_start = (char *)pool; big_chunk_end = big_chunk_start + (n-1); #ifdef MEMORY_TRACE #ifndef CHECK_ONLY memory_base = (intptr_t)pool; memory_size = n; memory_count = 0; memory_map = (unsigned char *)(*malloc_hook)(n/32 + 16); if (memory_map != 0) { memset(memory_map, 0, n/32+8); memory_file = fopen(memfile, "wb"); if (memory_file == NULL) { (*free_hook)(memory_map); memory_map = 0; } else { n = n/32 + 8; putc(0, memory_file); putc(0, memory_file); putc(0, memory_file); /* 3 bytes to overwrite later on */ putc(n, memory_file); putc(n>>8, memory_file); putc(n>>16, memory_file); memory_comment(2); /* startup code */ init_flags &= ~INIT_EXPANDABLE; } } #endif #endif nilsegment = (Lisp_Object *)pool; pool = pool + NIL_SEGMENT_SIZE; #ifdef COMMON /* NB here that NIL is tagged as a CONS not as a symbol */ C_nil = doubleword_align_up(nilsegment) + TAG_CONS + 8; #else C_nil = doubleword_align_up(nilsegment) + TAG_SYMBOL; #endif /* * If at the end of the run I am going to free some space I had better not * free these pages. When I free the nilsegment they all get discarded at * once. */ while (pages_count < free_space) { void *page = (void *)&pool[pages_count*(CSL_PAGE_SIZE+16)]; pages[pages_count++] = page; } } } } if (nilsegment != NULL && pages_count > 0) { if (stack_segsize != 1) { stacksegment = (Lisp_Object *)my_malloc(stack_segsize*CSL_PAGE_SIZE + 16); if (stacksegment == NULL) { fatal_error(err_no_store); } } stacksegment = (Lisp_Object *)pages[--pages_count]; } else { printf("pages_count <= 0 = %d\n", pages_count); fatal_error(err_no_store); } CSL_MD5_Update((unsigned char *)memfile, 8); /* * The stack does not need to be doubleword aligned, but it does need * to be word aligned (otherwise certain back-pointers in the garbage * collector give trouble), so I fix it up here. Note that stacksegment * remains pointing at the original base so that I can free() it later. */ stackbase = (Lisp_Object *)doubleword_align_up((intptr_t)stacksegment); } #ifdef EXPLICIT_FREE_AT_END_OF_RUN /* * In general I will let CSL exit without bothering to free up all the * memory that it allocated - that job can be left (to the extent that * it is needed at all) to the run-time system. But if for some reason * you really mind about such things here is some code to do it for you... */ static void abandon(void *p[], int32_t n) { while (n != 0) { void *w = p[--n]; /* * The test here that avoids calling free on a NULL pointer is * certainly not needed with an ANSI compliant library - but * rumour has it that many Unix libraries are unkind in this * respect, and the test is pretty cheap... */ if (w != NULL) my_free(w); } } #endif void drop_heap_segments(void) { #ifdef MEMORY_TRACE #ifndef CHECK_ONLY identify_page_types(); #endif #endif #ifdef EXPLICIT_FREE_AT_END_OF_RUN abandon(pages, pages_count); abandon(heap_pages, heap_pages_count); abandon(vheap_pages, vheap_pages_count); abandon(bps_pages, bps_pages_count); abandon(native_pages, native_pages_count); my_free(stacksegment); my_free(nilsegment); #endif #ifdef MEMORY_TRACE #ifndef CHECK_ONLY fseek(memory_file, 0L, SEEK_SET); putc(memory_records & 0xff, memory_file); putc((memory_records>>8) & 0xff, memory_file); putc((memory_records>>16) & 0xff, memory_file); fclose(memory_file); memory_file = NULL; memory_map = NULL; #endif #endif } static char *find_checksum(char *name, int32_t len, const setup_type *p) { char *n; while (p->name != NULL) p++; n = (char *)p->one; if (strlen(n) == (size_t)len && memcmp(name, n, len) == 0) return (char *)p->two; else return NULL; } static Lisp_Object MS_CDECL Lcheck_c_code(Lisp_Object nil, int nargs, ...) { Lisp_Object name, lc1, lc2, lc3; int32_t c1=-1, c2=-1, c3=-1; long int x1=-2, x2=-2, x3=-2; int32_t len; va_list a; char *p; char *sname; argcheck(nargs, 4, "check-c-code"); va_start(a, nargs); name = va_arg(a, Lisp_Object); lc1 = va_arg(a, Lisp_Object); lc2 = va_arg(a, Lisp_Object); lc3 = va_arg(a, Lisp_Object); va_end(a); if (!is_vector(name) || type_of_header(vechdr(name)) != TYPE_STRING || !is_fixnum(lc1) || !is_fixnum(lc2) || !is_fixnum(lc3)) return aerror1("check-c-code", name); c1 = int_of_fixnum(lc1); c2 = int_of_fixnum(lc2); c3 = int_of_fixnum(lc3); sname = &celt(name, 0); len = length_of_header(vechdr(name)) - CELL; /* * trace_printf("+++ Checking %.*s %d %d %d\n", * (int)len, sname, c1, c2, c3); */ p = find_checksum(sname, len, u01_setup); if (p == NULL) p = find_checksum(sname, len, u02_setup); if (p == NULL) p = find_checksum(sname, len, u03_setup); if (p == NULL) p = find_checksum(sname, len, u04_setup); if (p == NULL) p = find_checksum(sname, len, u05_setup); if (p == NULL) p = find_checksum(sname, len, u06_setup); if (p == NULL) p = find_checksum(sname, len, u07_setup); if (p == NULL) p = find_checksum(sname, len, u08_setup); if (p == NULL) p = find_checksum(sname, len, u09_setup); if (p == NULL) p = find_checksum(sname, len, u10_setup); if (p == NULL) p = find_checksum(sname, len, u11_setup); if (p == NULL) p = find_checksum(sname, len, u12_setup); if (p == NULL) return aerror1("check-c-code", name); if (sscanf(p, "%ld %ld %ld", &x1, &x2, &x3) != 3) return aerror("check-c-code"); if (c1 == x1 && c2 == x2 && c3 == x3) return onevalue(nil); err_printf("\n+++++ C code and environment files not compatible\n"); err_printf("please check, re-compile and try again\n"); return aerror("check-c-code"); } static setup_type const restart_setup[] = /* * things that are in modules that do not define enough Lisp entrypoints * to be worth giving separate entry-tables. */ { {"check-c-code", wrong_no_na, wrong_no_nb, Lcheck_c_code}, {"define-in-module", Ldefine_in_module, too_many_1, wrong_no_1}, {"modulep", Lmodule_exists, too_many_1, wrong_no_1}, {"start-module", Lstart_module, too_many_1, wrong_no_1}, {"write-module", Lwrite_module, too_many_1, wrong_no_1}, {"copy-module", Lcopy_module, too_many_1, wrong_no_1}, {"copy-native", too_few_2, Lcopy_native, wrong_no_2}, {"delete-module", Ldelete_module, too_many_1, wrong_no_1}, {"load-module", Lload_module, too_many_1, wrong_no_1}, {"list-modules", wrong_no_na, wrong_no_nb, Llist_modules}, {"writable-libraryp", Lwritable_libraryp, too_many_1, wrong_no_1}, {"library-members", Llibrary_members, too_many_1, Llibrary_members0}, {"startup-banner", Lbanner, too_many_1, wrong_no_1}, {"instate-c-code", too_few_2, Linstate_c_code, wrong_no_2}, /* An embedded help system that used to exist has now been disabled */ #if 0 {"write-help-module", too_few_2, Lwrite_help_module, wrong_no_2}, {"help", Lhelp, Lhelp_2, Lhelp_n}, {"?", Lhelp, too_many_1, wrong_no_1}, #endif {"set-help-file", too_few_2, Lset_help_file, wrong_no_2}, {"mapstore", Lmapstore, too_many_1, Lmapstore0}, {"verbos", Lverbos, too_many_1, wrong_no_1}, #ifdef COMMON {"errorset", Lerrorset1, Lerrorset2, Lerrorsetn}, {"gc", Lgc, too_many_1, Lgc0}, #else {"errorset", Lerrorset1, Lerrorset2, Lerrorsetn}, {"reclaim", Lgc, too_many_1, Lgc0}, #endif {NULL, 0, 0, 0} }; static void create_symbols(setup_type const s[], CSLbool restartp) { int i; for (i=0; s[i].name != NULL; i++) make_symbol(s[i].name, restartp, s[i].one, s[i].two, s[i].n); } static int32_t defined_symbols; static void count_symbols(setup_type const s[]) { int i; for (i=0; s[i].name != NULL; i++) defined_symbols++; } static void set_up_variables(CSLbool restartp, int isdemo); static setup_type_1 *find_def_table(Lisp_Object mod, Lisp_Object checksum); typedef struct dynamic_modules { char *name; setup_type_1 *entries; } dynamic_modules; static dynamic_modules *loaded_dynamic_modules = NULL; static unsigned int loaded_dynamic_count = 0 , loaded_dynamic_size = 0; /* * A real curiosity of my implementation is that find_dynamic_module * takes a char * and a length. The "string" it is given need not be * properly terminated with a "\0". The string data might be transient. * in contrase, record_dynamic_module takes a normal-style C string (which * of course is terminated with '\0', and it requires that the string * data is non-transient. BEWARE if you try to use these at some stage in the * future. */ static setup_type_1 *find_dynamic_module(char *name, int32_t len) { unsigned int hash = 0; int i; char *p = name; if (loaded_dynamic_size == 0) return NULL; for (i=0; i<len; i++) hash=169*hash+(*p++ & 0xff); hash %= loaded_dynamic_size; for (;;) { if (loaded_dynamic_modules[hash].name == NULL) return NULL; if (strncmp(name, loaded_dynamic_modules[hash].name, len) == 0 && strlen(loaded_dynamic_modules[hash].name) == len) return loaded_dynamic_modules[hash].entries; hash = (hash + 1) % loaded_dynamic_size; } } /* * The constant here must be a prime number. */ #define INITIAL_DYNAMIC_MODULE_HASH_SIZE 1009 static void record_dynamic_module(char *name, setup_type_1 *entries) { unsigned int hash; char *p; loaded_dynamic_count++; if (3*loaded_dynamic_count >= 2*loaded_dynamic_size) { dynamic_modules *newtable; unsigned int newsize; unsigned int i; if (loaded_dynamic_size == 0) newsize = INITIAL_DYNAMIC_MODULE_HASH_SIZE; else { newsize = 2*loaded_dynamic_size-1; while (!primep(newsize)) newsize+=2; } #ifdef TRACE_NATIVE trace_printf("Hash needs to grow from %d to %d\n", loaded_dynamic_size, newsize); ensure_screen(); #endif newtable = (dynamic_modules *) malloc(newsize*sizeof(dynamic_modules)); for (i=0; i<newsize; i++) newtable[i].name = NULL; for (i=0; i<loaded_dynamic_size; i++) { if ((p = loaded_dynamic_modules[i].name) == NULL) continue; hash = 0; while (*p != 0) hash=169*hash+(*p++ & 0xff); /* * I will leave the trace print here when I rehash so that I spot cases of * rehashing in case to increase the chance of spotting associated bugs. * I will also start with a small hash table so that repeated rehashing is * provoked. */ #ifdef TRACE_NATIVE trace_printf("Hash for %s is %x in REHASH\n", loaded_dynamic_modules[i].name, hash); ensure_screen(); #endif hash %= newsize; for (;;) { if (newtable[hash].name == NULL) { newtable[hash].name = loaded_dynamic_modules[i].name; newtable[hash].entries = loaded_dynamic_modules[i].entries; break; } hash = (hash + 1) % newsize; } } if (loaded_dynamic_size != 0) free(loaded_dynamic_modules); loaded_dynamic_modules = newtable; loaded_dynamic_size = newsize; } p = name; hash = 0; while (*p != 0) hash=169*hash+(*p++ & 0xff); hash %= loaded_dynamic_size; for (;;) { if (loaded_dynamic_modules[hash].name == NULL) { loaded_dynamic_modules[hash].name = name; loaded_dynamic_modules[hash].entries = entries; return; } if (strcmp(name, loaded_dynamic_modules[hash].name) == 0) { loaded_dynamic_modules[hash].entries = entries; return; } hash = (hash + 1) % loaded_dynamic_size; } } static void warm_setup(int isdemo) { /* * Here I need to read in the bulk of the checkpoint file. */ Lisp_Object nil = C_nil; int32_t i; Cfread((char *)&heap_pages_count, sizeof(heap_pages_count)); Cfread((char *)&vheap_pages_count, sizeof(vheap_pages_count)); Cfread((char *)&bps_pages_count, sizeof(bps_pages_count)); heap_pages_count = flip_bytes(heap_pages_count); vheap_pages_count = flip_bytes(vheap_pages_count); bps_pages_count = flip_bytes(bps_pages_count); /* * Here I want to arrange to have at least one free page after re-loading * an image. If malloc can give me enough I grab it here. Note that I do * not yet know how many pages will be needed for hard code, which is a * bit of a nuisance! */ i = heap_pages_count+vheap_pages_count+ bps_pages_count+1 - pages_count; #ifdef MEMORY_TRACE if (i > 0) fatal_error(err_no_store); #else while (i-- > 0) { void *page = my_malloc_1((size_t)(CSL_PAGE_SIZE + 16)); if (page == NULL) { fatal_error(err_no_store); } else pages[pages_count++] = page; } #endif { char dummy[16]; Cfread(dummy, 8); } #ifdef MEMORY_TRACE #ifndef CHECK_ONLY memory_comment(6); /* vector heap */ #endif #endif for (i=0; i<vheap_pages_count; i++) { intptr_t p; vheap_pages[i] = allocate_page(); p = doubleword_align_up((intptr_t)vheap_pages[i]); Cfread((char *)p, CSL_PAGE_SIZE); } { char dummy[16]; Cfread(dummy, 8); } #ifdef MEMORY_TRACE #ifndef CHECK_ONLY memory_comment(5); /* cons heap */ #endif #endif for (i=0; i<heap_pages_count; i++) { intptr_t p; heap_pages[i] = allocate_page(); p = quadword_align_up((intptr_t)heap_pages[i]); Cfread((char *)p, CSL_PAGE_SIZE); } { char dummy[16]; Cfread(dummy, 8); } #ifdef MEMORY_TRACE #ifndef CHECK_ONLY memory_comment(14); /* BPS heap */ #endif #endif for (i=0; i<bps_pages_count; i++) { intptr_t p; bps_pages[i] = allocate_page(); p = doubleword_align_up((intptr_t)bps_pages[i]); Cfread((char *)p, CSL_PAGE_SIZE); } { char endmsg[32]; Cfread(endmsg, 24); /* the termination record */ /* * Although I check here I will not make the system crash if I see an * error - at least until I have tested things and found this test * properly reliable. */ #ifdef COMMON if (strncmp(endmsg, "\n\nEnd of CCL dump file\n\n", 24) != 0) #else if (strncmp(endmsg, "\n\nEnd of CSL dump file\n\n", 24) != 0) #endif { term_printf("\n+++ Bad end record |%s|\n", endmsg); } } /* * There is a delicacy here - Cfread uses Iread to read chunks of * data from the real input file, but it never goes beyond the recorded * end of file mark. This buffering ensures that at this stage any * pending part-word of data will have been read - this because the * read buffer used is a multiple of 4 bytes long. This point matters * with regard to checksum validation on these files. */ crypt_active = -1; /* Have read all of the initial image file */ IcloseInput(YES); #ifdef MEMORY_TRACE #ifndef CHECK_ONLY memory_comment(9); /* adjusting */ #endif #endif inject_randomness((int)clock()); adjust_all(); #ifdef MEMORY_TRACE #ifndef CHECK_ONLY memory_comment(12); /* remainder of setup */ #endif #endif eq_hash_tables = eq_hash_table_list; equal_hash_tables = equal_hash_table_list; eq_hash_table_list = equal_hash_table_list = nil; { Lisp_Object qq; for (qq = eq_hash_tables; qq!=nil; qq=qcdr(qq)) rehash_this_table(qcar(qq)); for (qq = equal_hash_tables; qq!=nil; qq=qcdr(qq)) rehash_this_table(qcar(qq)); } gensym_ser = flip_bytes(gensym_ser); print_precision = flip_bytes(print_precision); miscflags = flip_bytes(miscflags); current_modulus = flip_bytes(current_modulus); fastget_size = flip_bytes(fastget_size); package_bits = flip_bytes(package_bits); set_up_functions(1); set_up_variables(1, isdemo); /* * Now I have closed the main heap image, but if there is any hard machine * code available for this architecture I should load it. When I do this * the main heap has been loaded and relocated and all the entrypoints * in it that relate to kernel code have been inserted. */ if (native_code_tag != 0) /* Not worth trying if none available */ { if (!IopenRoot(NULL, -native_code_tag, 0)) { int32_t nn = Igetc() & 0xff; nn = nn + ((Igetc() & 0xff) << 8); native_pages_count = nn; for (i=0; i<native_pages_count; i++) { intptr_t p; /* * Because I did not know earlier how many pages would be needed here I * may not have overall enough. So I expand my heap (if possible) * when things start to look tight here. */ if (pages_count <= 1) { void *page = my_malloc_1((size_t)(CSL_PAGE_SIZE + 16)); if (page == NULL) { fatal_error(err_no_store); } else pages[pages_count++] = page; } native_pages[i] = allocate_page(); p = (intptr_t)native_pages[i]; p = doubleword_align_up(p); fread_count = 0; Cfread((char *)p, CSL_PAGE_SIZE); native_fringe = car32(p); relocate_native_code((unsigned char *)p, native_fringe); } IcloseInput(YES); } } /* * With a warm start I must instate the definitions of all functions * that may have been compiled into hard code on this platform. Functions that * may be hard-coded on SOME platform may also be in a mess and will have * a byte-coded definition put back in place at this point. Observe that this * happens AFTER the system has otherwise been loaded and relocated. */ { Lisp_Object f_list = native_code, byte_code_def; do_not_kill_native_code = 1; while (f_list != nil) { Lisp_Object w, fn, defs; int32_t nargs; int instated_something = 0; byte_code_def = nil; w = qcar(f_list); f_list = qcdr(f_list); fn = qcar(w); w = qcdr(w); nargs = int_of_fixnum(qcar(w)); defs = qcdr(w); while (defs != nil) { int32_t n, tag, type, off; intptr_t page; void *e; w = qcar(defs); defs = qcdr(defs); n = int_of_fixnum(qcar(w)); w = qcdr(w); tag = (n >> 20) & 0xff; type = (n >> 18) & 0x3; page = n & 0x3ffff; if (tag == 0) { byte_code_def = qcdr(w); continue; } if (tag != native_code_tag) continue; /* Not for me today */ instated_something = 1; off = int_of_fixnum(qcar(w)); w = qcdr(w); /* * Now fn should be a symbol, the function to be defined. w is the thing to go * into its environment cell. page and off define a location in the hard * code space and type tells me which of the 3 function cells to put that in. * * I will not (yet) mess around with the removal of C definition * flags and all the other delicacies. Note that this means attempts to * redefine built-in functions with user-provided native code varients * may cause all sorts of muddle! Please do not try it, but when you * do (!) tell me and I will attempt to work out what ought to happen. * Maybe it will all be OK provided that a consistent byte-code definition * is in place before any native code gets generated. */ page = (intptr_t)native_pages[page]; page = doubleword_align_up(page); e = (void *)((char *)page + off); switch (type) { /* * Warning - I just support nargs being a simple integer here, with no * fancy encoding for variable numbers of args or &rest args etc. I think * that for native code all such cases need to be dealt with via non-zero * type code so that the 3 individual function cells get filled in one * by 1. */ case 0: switch (nargs) { case 0: set_fns(fn, wrong_no_0a, wrong_no_0b, (n_args *)e); break; case 1: set_fns(fn, (one_args *)e, too_many_1, wrong_no_1); break; case 2: set_fns(fn, too_few_2, (two_args *)e, wrong_no_2); break; case 3: set_fns(fn, wrong_no_3a, wrong_no_3b, (n_args *)e); break; default:set_fns(fn, wrong_no_na, wrong_no_nb, (n_args *)e); break; } break; /* * A non-zero type field allows me to fill in just one of the function cells. * Note that I ought to arrange to get ALL of them filled in somehow, either * by using type=0 or by using all three of type = 1,2,3. */ case 1: ifn1(fn) = (intptr_t)e; break; case 2: ifn2(fn) = (intptr_t)e; break; case 3: ifnn(fn) = (intptr_t)e; break; } qenv(fn) = w; } if (!instated_something && byte_code_def != nil) { w = cons(fixnum_of_int(nargs), byte_code_def); /* * You can look at this bit of code and moan, saying "What happens if * the call to CONS causes a garbage collection?". Well I have this policy * that garbage collection attempts during startup should be thought of * as fatal, and that the user should give enough memory to make it possible * to get at least started. I hope that I do not generate much litter here * and in other places within the startup code. Not thinking about GC * safety leaves the code neater and easier to work with. */ Lsymbol_set_definition(nil, fn, w); } } do_not_kill_native_code = 0; } /* * The stuff above is about the internal native compilation that I am no * longer pursuing. Well I may look back at it some day, but it would * involve CSL itselh having compiler back-ends for all relevant architectures * and now I am moving to using a local C compiler to do that stuff. */ { Lisp_Object n = native_defs; char *p; while (n != nil) { Lisp_Object w, name, mod, fname, env, env1, checksum; setup_type_1 *table, *tp; uint32_t *pp; int32_t len; name = qcar(n); n = qcdr(n); w = get(name, nativecoded_symbol); if (consp(w)) { mod = qcar(w); w = qcdr(w); if (consp(w)) { fname = qcar(w); w = qcdr(w); if (consp(w)) { checksum = qcar(w); env = qcdr(w); } else continue; } else continue; } else continue; /* * If I get here I have * name the Lisp symbol that may get a native definition * mod a string that names the module it lives in * fname the name of the function in the native code to load * env an environment to give the native definition * checksum module checksum * name and fname may differ, for instance fname is the name that the * function had when it was compiled, but a copy of the definition may * have been copied to name... */ #ifdef TRACE_NATIVE trace_printf("Possible native def: "); prin_to_trace(name); trace_printf("\nmodule: "); prin_to_trace(mod); trace_printf("\nfname: "); prin_to_trace(fname); trace_printf("\nEnv: "); prin_to_trace(env); trace_printf("\nChecksum: "); prin_to_trace(checksum); trace_printf("\n"); #endif /* * First I will try to ensure that the module concerned gets loaded. It * may have been already, in which case I just need its handle. */ push4(name, fname, env, n); table = find_def_table(mod, checksum); pop4(n, env, fname, name); if (table == NULL) continue; /* This module is not available */ #ifdef TRACE_NATIVE trace_printf("setup table at %p\n", table); #endif /* Now seek for fname in there... */ tp = table; while (tp->name != NULL) tp++; #ifdef SOON modname = "???"; if (strcmp(modname, (char *)tp->one) != 0) { trace_printf("Module name %s disagrees with %s\n", modname, (char *)tp->one); continue; } #else #ifdef DEBUG_NATIVE modname = "???"; trace_printf("module itself says it is called %s, wants to be %s\n", (char *)tp->one, modname); #endif #endif push4(name, fname, env, n); p = get_string_data(fname, "restart:native_code", &len); pop4(n, env, fname, name); nil = C_nil; if (exception_pending()) continue; while (tp!=table) { tp--; if (strncmp(p, tp->name, len) == 0 && strlen(tp->name)==len) { p = NULL; break; } } if (p != NULL) continue; /* * I will ONLY install native code if I have a bytecoded version in place * already. Note that I will require the function now about to be * redefined to have a bytecoded form that agrees wrt a checksum with the * native code version from the dynamically loaded module. * WELL there is an issue about the tail-call specials. They have a * symbol in the env cell and no checksum for me to look at at all. I * think I will just trust things in those cases. */ env1 = qenv(name); #ifdef TRACE_NATIVE prin_to_trace(env1); trace_printf(" is the bytecoded version\n"); #endif if (!is_symbol(env)) { if (!consp(env1) || !is_bps(qcar(env1))) continue; env1 = qcdr(env1); if (!is_vector(env1)) continue; env1 = Lgetv(nil, env1, Lupbv(nil, env1)); #ifdef TRACE_NATIVE prin_to_trace(env1); trace_printf(" should be checksum again\n"); #endif if (!is_numbers(env1) || !is_bignum(env1)) continue; pp = bignum_digits(env1); #ifdef TRACE_NATIVE trace_printf("%u %u vs %u %u\n", pp[0], pp[1], tp->c2, tp->c1); #endif if (pp[0] != tp->c2 || pp[1] != tp->c1) continue; } if (load_limit != 0x7fffffff) { if (load_count >= load_limit) continue; prin_to_trace(name); trace_printf(" : %d\n", load_count++); } /* * Gosh: now I can actually make the function available to users! */ #ifdef TRACE_NATIVE trace_printf("actually set up native function\n"); #endif /* * The symbol I am about to define is already on native_defs and * has all the property-list info that it needs, so I am in the * happy situation of not needing to do much here. */ ifn1(name) = (intptr_t)tp->one; ifn2(name) = (intptr_t)tp->two; ifnn(name) = (intptr_t)tp->n; qenv(name) = env; } } inject_randomness((int)clock()); } static char dll_cache_directory[LONGEST_LEGAL_FILENAME] = {0}; static void find_dll_cache_directory() { unsigned char md[16]; char userinfo[80], counts[8]; int i; #ifdef WIN32 DWORD n; #endif char *p; struct stat stbuf; int count; if (dll_cache_directory[0] != 0) return; /* * This does its real work just once. But I may need to re-try * if the first choice directory name does not work well. */ for (count=0; count<100; count++) { CSL_MD5_Init(); sprintf(counts, "%d:", count); CSL_MD5_Update((unsigned char *)counts, strlen(counts)); CSL_MD5_Update((unsigned char *)fwin_full_program_name, strlen(fwin_full_program_name)); #ifdef WIN32 userinfo[0] = ';'; n = sizeof(userinfo) - 1; if (!GetUserName(userinfo+1, &n)) strcpy(userinfo, ";UnknownUser;"); else strcat(userinfo, ";"); if (GetTempPath(LONGEST_LEGAL_FILENAME, dll_cache_directory) == 0) strcpy(dll_cache_directory, ".\\"); #else sprintf(userinfo, ";%d;", geteuid()); strcpy(dll_cache_directory, "/tmp/"); #endif CSL_MD5_Update((unsigned char *)userinfo, strlen(userinfo)); CSL_MD5_Update((unsigned char *)linker_type, strlen(linker_type)); CSL_MD5_Final(md); #ifdef TRACE_NATIVE trace_printf("Base cache name on %s %s %s\n", fwin_full_program_name, userinfo, linker_type); #endif p = dll_cache_directory + strlen(dll_cache_directory); /* * The name of the directory that I invent will be the letters * CSL followed by 25 characters (0-9, a-t) (ie 25*5-125 bits derived * from an MD5 checksum). */ *p++ = 'C'; *p++ = 'S'; *p++ = 'L'; for (i=0; i<25; i++) { int j, w = 0; for (j=15; j>=0; j--) { int w1 = (md[j] >> 5) | (w << 3); w = md[j] & 0x1f; md[j] = w1; } if (w < 10) *p++ = '0' + w; else *p++ = 'a' + w - 10; } *p = 0; #ifdef TRACE_NATIVE trace_printf("DLL cache directory will be %s\n", dll_cache_directory); #endif /* * I should now verify that that directory exists and is readable and * writable! If it is I am done. If not I will try to create it as * a directory - if that works I can return. If that still does not help * I will loop to try a second-choice name. If the "temporary directory" * that I obtained did not exist this might loop I suppose, so anybody * who sets the shell variable TEMP to something silly might get hurt? To * avoid infinite pain I will just declare disaster if I do not succeed in * a fair number of tries. */ if (stat(dll_cache_directory, &stbuf) == 0 && #ifdef S_IRUSR stbuf.st_mode & S_IRUSR && #endif #ifdef S_IWUSR stbuf.st_mode & S_IWUSR && #endif (stbuf.st_mode & S_IFMT) == S_IFDIR) return; Cmkdir(dll_cache_directory); if (stat(dll_cache_directory, &stbuf) == 0 && #ifdef S_IRUSR stbuf.st_mode & S_IRUSR && #endif #ifdef S_IWUSR stbuf.st_mode & S_IWUSR && #endif (stbuf.st_mode & S_IFMT) == S_IFDIR) return; } /* * here 100 different attempts to find a suitable directory have all * failed. I just give up! */ fatal_error(err_no_tempdir); } static char objname[LONGEST_LEGAL_FILENAME]; static void tidy_up_old_dlls(const char *name, int why, long int size) { const char *p = name, *q = objname; /* * If the file I have found has a name rather like objname then I will delete * it. So I will start to scanning past initial equal parts in the names. */ while ((*p)==(*q) && (*p)!=0) { p++; q++; } /* * Now if p is of the form (where nnn is numeric) * nnn-nnn-nnn.dll or nnn-nnn-nnn.so * it is an old DLL for the same module so it should go. I have * some fairly grotty code here that is intended to detect this * pattern. Well it is a bit messier than that - the first few chars of the * checksum info may have matched... */ while (*p != 0 && isdigit(*p)) p++; if (*p == '-') p++; while (*p != 0 && isdigit(*p)) p++; if (*p == '-') p++; while (*p != 0 && isdigit(*p)) p++; if (strcmp(p, ".dll") != 0 && strcmp(p, ".so") != 0) return; #ifdef TRACE_NATIVE trace_printf("Deleting old DLL file %s\n", name); #endif remove(name); } static setup_type_1 *find_def_table(Lisp_Object mod, Lisp_Object checksum) { int32_t len, checklen; char *sname, *checkname; char modname[80], xmodname[LONGEST_LEGAL_FILENAME]; char sname1[LONGEST_LEGAL_FILENAME]; Ihandle save; FILE *dest; int c; Lisp_Object nil = C_nil; char setupname[80]; char *p; setup_type_1 *dll; initfn *init; #ifdef WIN32 HANDLE a; UINT ww; #else void *a; #endif #ifdef TRACE_NATIVE trace_printf("find_def_table "); prin_to_trace(mod); trace_printf("\n"); ensure_screen(); #endif sname = get_string_data(mod, "find_def_table", &len); nil = C_nil; if (exception_pending()) return NULL; checkname = get_string_data(checksum, "find_def_table", &checklen); nil = C_nil; if (exception_pending()) return NULL; #ifdef TRACE_NATIVE trace_printf("Checksum given as \"%.*s\"\n", checklen, checkname); #endif sprintf(sname1, "%.*s-%.*s", (int)len, sname, (int)checklen, checkname); p = sname1; while (*p!=0) { if (*p == ' ') *p = '-'; p++; } dll = find_dynamic_module(sname1, strlen(sname1)); if (dll != NULL) return dll; sprintf(modname, "%.*s/%s", (int)len, sname, linker_type); /* * Here I will do some more cache-style activity. I will hold a * dirctory typically called /tmp/nnnnnn (where nnnnn is a checksum * on fwin_full_program_name and the linker type and the curren user) * and put extracted DLL files there. * If I find one present there I will use it. Otherwise I * will extract it from the image file. This may give me trouble * with regard to versioning, and so when I initially create or update * a file in the image I should delete any cached version as outdated. * (that last bit not done to start with) */ find_dll_cache_directory(); #ifdef TRACE_NATIVE trace_printf("Attempt to load module %s\n", modname); #endif /* * Now if dll_cache_directory/sname.[so/dll] exists I will use it. * otherwise I will create it by copying from the image file. * The name I use here will include checksum information. At some stage * I should possibly try to delete any files in the cache that match in * their root but disagree in the checksum portion, since they are liable * to be old. */ #ifdef WIN32 sprintf(objname, "%s\\%s.dll", dll_cache_directory, sname1); #else sprintf(objname, "%s/%s.so", dll_cache_directory, sname1); #endif #ifdef TRACE_NATIVE trace_printf("Invented name %s for temp location of module\n", objname); #endif { struct stat stbuf; /* * Check if the module exists in the cache - if not try to create it... * I count the DLL as unavailable if either stat fails (which may indicate * that the file does not exist) or if it is not readable by its owner * (who ought to be me!). Not if it is not readable it may not be writable * either, and in that case the attempt here to create it will fail. */ if (stat(objname, &stbuf) != 0 #ifdef S_IRUSR || (stbuf.st_mode & S_IRUSR) == 0 #endif ) { Icontext(&save); if (Iopen(modname, strlen(modname), 1, xmodname)) { Irestore_context(save); trace_printf("module not found\n"); return NULL; } #ifdef TRACE_NATIVE trace_printf("Will now copy %s to the DLL cache\n", modname); #endif /* * Here I can tidy up the cache directory. I want to DELETE any files in * it whose names are somewhat similar to the one I am about to create. * Just for now I will just print a message ratherthan actually do anything. */ set_hostcase(1); scan_files(dll_cache_directory, tidy_up_old_dlls); /* * Here I can read and process the module... */ dest = fopen(objname, "wb"); if (dest == NULL) /* failed to write to temp file */ { IcloseInput(0); Irestore_context(save); return NULL; } while ((c = Igetc()) != EOF) putc(c, dest); IcloseInput(0); Irestore_context(save); if (fclose(dest) != 0) { trace_printf("failed to write DLL to temp directory\n"); return NULL; } } } /* * Now I have copied the object file data to a "real" but temporary file. */ sprintf(modname, "%.*s", (int)len, sname); #ifdef TRACE_NATIVE trace_printf("load_dynamic for find_def_table %s %s\n", objname, modname); #endif sprintf(setupname, "%s_setup", modname); for (p=setupname; *p!=0; p++) if (*p=='-') *p='_'; #ifdef TRACE_NATIVE trace_printf("Look for \"%s\"\n", setupname); #endif #ifdef WIN32 /* * In various cases of failure Windows has a default behaviour of popping * up a dialog box when a DLL can not be loaded. I do not want that, since * I intend to recover graciously if the module can not be located or * loaded. */ ww = SetErrorMode(SEM_FAILCRITICALERRORS); #ifdef TRACE_NATIVE trace_printf("Loading DLL called %s for %s\n", objname, modname); #endif a = LoadLibrary(objname); if (a == 0) { DWORD err = GetLastError(); char errbuf[80]; /* * If I let Windows pop up its message box I still seem to get more info * than FormatMessage presents me with... Specifically if the module I tried * to load refused to because of a symbol that it needed to load, the * pop up tells me the name of that symbol. */ err = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, errbuf, 80, NULL); if (err != 0) trace_printf("%s", errbuf); } SetErrorMode(ww); #ifdef TRACE_NATIVE trace_printf("Dynamic loading of test code\na = %p\n", (void *)a); #endif if (a == 0) return 0; dll = (setup_type_1 *)GetProcAddress(a, setupname); /* * The dynamic module that I create should always have a function called * "init" that I must call to tell it where nil, stack and stacklimit are. */ init = (initfn *)GetProcAddress(a, "init"); #else a = dlopen(objname, RTLD_NOW | RTLD_GLOBAL); #ifdef TRACE_NATIVE trace_printf("a = %p\n", a); #endif if (a == NULL) { trace_printf("Err = <%s>\n", dlerror()); fflush(stdout); return 0; } dll = (setup_type_1 *)dlsym(a, setupname); init = (initfn *)dlsym(a, "init"); #endif #ifdef TRACE_NATIVE trace_printf("setup table is %p, init fn is %p\n", dll, init); #endif if (dll == NULL || init == NULL) { #ifdef WIN32 FreeLibrary(a); #endif return NULL; } (*init)(&C_nil, &C_stack, &stacklimit); /* * Wheee - I have now loaded and initialised the module. */ #ifdef TRACE_NATIVE { setup_type_1 *b = dll; while (b->name != NULL) { trace_printf("%s %p %p %p %u %u\n", b->name, b->one, b->two, b->n, b->c1, b->c2); b++; } trace_printf("%s %s\n", (char *)(b->one), (char *)(b->two)); } #endif /* * remove(objname); * At one stage I wanted to count the DLL files as temporary - but now I keep * them all in a cache directory, so I really do NOT want to delete them * here... If the user deletes them that will not be a problem - they will get * re-created if necessary. */ /* * Update the cache... */ p = (char *)malloc(strlen(sname1)+1); strcpy(p, sname1); p[len] = 0; record_dynamic_module(p, dll); return dll; } int setup_dynamic(setup_type_1 *dll, char *modname, Lisp_Object name, Lisp_Object fns) { char *p; setup_type_1 *b; int32_t len; Lisp_Object nil = C_nil, xchecksum; int32_t all_ok = 1; #ifdef TRACE_NATIVE trace_printf("setup_dynamic %s\n", modname); /* prin_to_trace(fns); */ trace_printf("\n"); #endif if (!consp(fns)) return 0; #ifdef TRACE_NATIVE b = dll; while (b->name != NULL) { trace_printf("%s %p %p %p %u %u\n", b->name, b->one, b->two, b->n, b->c1, b->c2); b++; } trace_printf("%s %s\n", (char *)(b->one), (char *)(b->two)); #endif /* * First I will check if the module loaded appears to match against the set * of functions I am expecting from it... */ b = dll; while (b->name != NULL) b++; /* * now b->one is expected to match modname, and b->two is expected * to match the string that is the first item in fns. */ if (strcmp(modname, (char *)b->one) != 0) { trace_printf("Module name %s disagrees with %s\n", modname, (char *)b->one); return 0; } p = get_string_data(qcar(fns), "instate_c_code", &len); nil = C_nil; if (exception_pending()) return 0; if (strncmp(p, (char *)b->two, len) != 0) { trace_printf("Module signature %.*s disagrees with %s\n", (int)len, p, (char *)b->two); return 0; } xchecksum = qcar(fns); fns = qcdr(fns); b = dll; /* * Now the table b and the list fns ought to match up. The list will have * entries * (name (e1 e2 ... en) . check) * where the name is the name of a Lisp function and the list needs * turning into a vector to go into its environment cell. * The table has columns * name f1 f2 n2 c1 c2 * where the name ought to match what is seen in the list, and then the * three functions go in the f1, f2 and fn cells. I will stop if I get * any mismatch at all - just to be cautious! */ while (consp(fns)) { Lisp_Object fname, env, env1, ww; if (b->name == NULL) { #ifdef TRACE_NATIVE trace_printf("Failed: setup table length problem\n"); #endif return 0; /* lengths of lists differ */ } env = qcar(fns); if (consp(env)) { fname = qcar(env); env = qcdr(env); if (consp(env)) { Lisp_Object chk = qcdr(env); uint32_t *pp; env = qcar(env); p = get_string_data(fname, "instate_c_code", &len); nil = C_nil; if (exception_pending()) { #ifdef TRACE_NATIVE trace_printf("Failed: get_string_data\n"); #endif return 0; } #ifdef TRACE_NATIVE trace_printf("instate next function %.*s vs %s\n", len, p, b->name); prin_to_trace(chk); trace_printf(" vs %u %u\n", b->c1, b->c2); #endif if (strncmp(p, b->name, len) != 0) { #ifdef TRACE_NATIVE trace_printf("Failed: name in setup table and env list differ\n"); #endif return 0; } /* * There is a small chance of misery here. The checksum MIGHT happen to * be a 1-word bignum or even a fixnum. If that happens the tests here will * reject it and the native code will not get instated. If this happens * the result can be a performance loss but it ought not to lead to * incorrect results, and if the checksum scheme is good it is only * expected to hit for around 1 in 10^9 functions that are processed, so * I will (for now) accept it. If I ever feel twitchy I will respond by * ensuring that md60 always returns a 2-word bignum result. Hmm I AM twitchy * and I have now done just that! */ if (!is_numbers(chk) || !is_bignum(chk)) { #ifdef TRACE_NATIVE trace_printf("Failed: checksum not a number or not big\n"); #endif return 0; } pp = bignum_digits(chk); #ifdef TRACE_NATIVE trace_printf("%u %u vs %u %u\n", pp[0], pp[1], b->c2, b->c1); #endif if (pp[0] != b->c2 || pp[1] != b->c1) { all_ok = 0; /* function's definition has changed? */ #ifdef TRACE_NATIVE trace_printf("Failed on a function: checksum discrepancy\n"); #endif goto next_def; } /* * I will ONLY install native code if I have a bytecoded version in place * already. I apply that rule to ensure that image files can be used across * different architectures. Well I will want to count tailcall magic as * OK. */ env1 = qenv(fname); #ifdef TRACE_NATIVE prin_to_trace(env1); trace_printf(" is the bytecoded version\n"); #endif if (qfn1(fname) == f1_as_0 || qfn1(fname) == f1_as_1 || qfn2(fname) == f2_as_0 || qfn2(fname) == f2_as_1 || qfn2(fname) == f2_as_2 || qfnn(fname) == f0_as_0 || qfnn(fname) == f3_as_0 || qfnn(fname) == f3_as_1 || qfnn(fname) == f3_as_2 || qfnn(fname) == f3_as_3) { if (!is_symbol(env1)) { all_ok = 0; /* malformed */ #ifdef TRACE_NATIVE prin_to_trace(fname); trace_printf(" Failed on a function: tailcall with env malformed\n"); #endif goto next_def; } } else { if (!consp(env1) || !is_bps(qcar(env1))) { all_ok = 0; /* no bytecoded version available */ #ifdef TRACE_NATIVE prin_to_trace(fname); trace_printf(" Failed on a function: no bytecoded version\n"); #endif goto next_def; } env1 = qcdr(env1); if (!is_vector(env1)) return nil; env1 = Lgetv(nil, env1, Lupbv(nil, env1)); #ifdef TRACE_NATIVE prin_to_trace(env1); trace_printf(" should be checksum again\n"); #endif if (!equal(env1, chk)) { all_ok = 0; /* bytecoded definition differs */ #ifdef TRACE_NATIVE trace_printf("Failed: bytecoded version checksum differs\n"); #endif goto next_def; } } nil = C_nil; if (exception_pending()) return 0; push2(name, fname); env = Llist_to_vector(nil, env); pop2(fname, name); nil = C_nil; if (exception_pending()) return 0; if (load_limit != 0x7fffffff) { if (load_count >= load_limit) { all_ok = 0; goto next_def; } prin_to_trace(fname); trace_printf(" :: %d\n", load_count++); } /* * Gosh: now I can actually make the function available to users! */ #ifdef TRACE_NATIVE trace_printf("actually set up native function\n"); #endif /* * I want to do a few things in addition to filling in the function and * environment cells... * (a) ensure that this symbol is in the list "native_defs"; * (b) give it a "bytecoded_symbol" property that captures all info about * the bytecode definition that I am displacing; * (c) give it a "nativecoded_symbol" property that should let me * re-instate this fast version of the code on subsequent runs when * the module loading must be repeated following a preserve/restart. */ ww = native_defs; while (consp(ww)) { if (qcar(ww) == fname) goto already_native; ww = qcdr(ww); } push4(name, fname, env, xchecksum); ww = cons(fname, native_defs); pop4(xchecksum, env, fname, name); nil = C_nil; if (exception_pending()) return 0; native_defs = ww; already_native: ww = Lsymbol_argcode(nil, fname); if (ww == nil) return 0; push4(name, fname, env, xchecksum); ww = cons(ww, qenv(fname)); pop4(xchecksum, env, fname, name); nil = C_nil; if (exception_pending()) return 0; push4(name, fname, env, xchecksum); putprop(fname, bytecoded_symbol, ww); pop4(xchecksum, env, fname, name); nil = C_nil; if (exception_pending()) return 0; push4(name, fname, env, xchecksum); ww = list3star(name, fname, xchecksum, env); pop4(xchecksum, env, fname, name); nil = C_nil; if (exception_pending()) return 0; push4(name, fname, env, xchecksum); putprop(fname, nativecoded_symbol, ww); pop4(xchecksum, env, fname, name); nil = C_nil; if (exception_pending()) return 0; ifn1(fname) = (intptr_t)b->one; ifn2(fname) = (intptr_t)b->two; ifnn(fname) = (intptr_t)b->n; qenv(fname) = env; } } next_def: fns = qcdr(fns); b++; } /* * At present I take the view that when a module has been loaded it will * be wanted for the rest of the Lisp run, and so I do not unload it... */ return 1; } /* * The next function is to do with compiling modules into machine * code (via C) and tben dynamically loading them. The first argument is * the name given to the module, which is the same as the name of the * FASL file I believe I am loading now. Furthermore the module * should (when loaded) define an external symbol called * <name>_setup * that is its table of functions that it defines. * * The second argument will be a * header string "int int int" followed by a list of triples * (name env . checksum) * where each name should be in the setup table from the file, and the * corresponding env is a list that needs to be converted to a vector and * placed in the symbol's environment cell. * * Note that the final entry in the setup table is of the form * NULL, "name", "int int int", 0 * and the name and triple of integers are expected to match the * information passed to instate_c_code. If they do not then the * modules concerned have somehow got out of step... */ Lisp_Object Linstate_c_code(Lisp_Object nil, Lisp_Object name, Lisp_Object fns) { /* * See if there is a module in the image file with the given name and * with its linker-tag matching the one for the current executable. If so * copy it to a temporary file called say t1.dll or t1.so. Dynamically load * it into memory. Keep the temporary file in a temporary directory but * where I might find it again next time I need it. Access a * symbol name_setup in it. The style of binary found should match the * information in the variable "linker_type". This version is to be called * by Lisp from a fasl-file as a module is loaded. The checksum information at * the start of "fns" will used in names for the .dll files and will be * recorded associated with the module name. */ int32_t len; char *sname; char modname[80]; int c; setup_type_1 *dll; #ifdef TRACE_NATIVE trace_printf("instate_c_code "); prin_to_trace(name); trace_printf("\n"); #endif if (!consp(fns)) return onevalue(nil); sname = get_string_data(name, "instate-c-code", &len); nil = C_nil; if (exception_pending()) return nil; dll = find_def_table(name, qcar(fns)); if (dll == NULL) return onevalue(nil); sprintf(modname, "%.*s", (int)len, sname); c = setup_dynamic(dll, modname, name, fns); return onevalue(c ? lisp_true : nil); } static void cold_setup(int isdemo) { Lisp_Object nil = C_nil; void *p; p = vheap_pages[vheap_pages_count++] = allocate_page(); vfringe = (Lisp_Object)(8 + (char *)doubleword_align_up((intptr_t)p)); vheaplimit = (Lisp_Object)((char *)vfringe + (CSL_PAGE_SIZE - 16)); p = heap_pages[heap_pages_count++] = allocate_page(); heaplimit = quadword_align_up((intptr_t)p); fringe = (Lisp_Object)((char *)heaplimit + CSL_PAGE_SIZE); heaplimit = (Lisp_Object)((char *)heaplimit + SPARE); codelimit = codefringe = 0; /* no BPS to start with */ miscflags = 3; qplist(nil) = nil; qfastgets(nil) = nil; qenv(nil) = nil; /* points to self in undefined case */ ifn1(nil) = (intptr_t)undefined1; ifn2(nil) = (intptr_t)undefined2; ifnn(nil) = (intptr_t)undefinedn; qheader(nil) = TAG_ODDS+TYPE_SYMBOL+SYM_SPECIAL_VAR; qvalue(nil) = nil; #ifdef COMMON qpname(nil) = make_string("NIL"); #else qpname(nil) = make_string("nil"); #endif qcount(nil) = 0; exit_tag = exit_value = nil; exit_reason = UNWIND_NULL; eq_hash_tables = equal_hash_tables = nil; current_package = nil; qvalue(nil) = getvector_init(sizeof(Package), nil); #ifdef COMMON qpackage(nil) = qvalue(nil); /* For sake of restart code */ all_packages = ncons(qvalue(nil)); #endif packhdr_(CP) = TYPE_STRUCTURE + (packhdr_(CP) & ~header_mask); #ifdef COMMON packname_(CP) = make_string("LISP"); #endif /* * The size chosen here is only an initial size - the hash table in a package * can grow later on if needbe - but I ought to ensure that the initial * size is big enough for the built-in symbols that Lisp creates in * this restart code. The size must be a power of 2. */ packint_(CP) = getvector_init(CELL+INIT_OBVECI_SIZE, fixnum_of_int(0)); packvint_(CP) = fixnum_of_int(1); packflags_(CP) = fixnum_of_int(++package_bits); #ifdef COMMON /* * Common Lisp also has "external" symbols to allow for... */ packnint_(CP) = fixnum_of_int(0); packext_(CP) = getvector_init(CELL+INIT_OBVECX_SIZE, fixnum_of_int(0)); packvext_(CP) = fixnum_of_int(1); packnext_(CP) = fixnum_of_int(1); /* Allow for nil */ { int i = (int)(hash_lisp_string(qpname(nil)) & (INIT_OBVECX_SIZE/CELL - 1)); elt(packext_(CP), i) = nil; } #else packnint_(CP) = fixnum_of_int(1); /* Allow for nil */ { int i = (int)(hash_lisp_string(qpname(nil)) & (INIT_OBVECI_SIZE/CELL - 1)); elt(packint_(CP), i) = nil; } #endif gensym_ser = 0; print_precision = 15; current_modulus = 1; fastget_size = 32; package_bits = 0; unset_var = nil; /* * there had better not be a need for garbage collection here... * ... or elsewhere in setup, since the world is not yet put together. * Ditto interrupts. */ #define boffo_size 256 boffo = getvector(TAG_VECTOR, TYPE_STRING, CELL+boffo_size); memset((void *)((char *)boffo + (CELL - TAG_VECTOR)), '@', boffo_size); #ifndef COMMON if (current_package == nil) { current_package = make_undefined_symbol("*package*"); qheader(current_package) |= SYM_SPECIAL_VAR; lisp_package = qvalue(current_package) = qvalue(nil); qvalue(nil) = nil; } #else /* * The next line has hidden depths. When it is obeyed during cold start * the C variable current_package has the value nil, hence make_symbol * looks in the value cell of nil to find the package to intern wrt. Once * this has been done I can put nil back how it ought to have been! */ current_package = make_undefined_symbol("*package*"); qheader(current_package)|= SYM_SPECIAL_VAR; lisp_package = qvalue(current_package) = qpackage(nil); qvalue(nil) = nil; /* Whew! */ #endif B_reg = nil; /* safe for GC */ unset_var = make_undefined_symbol("~indefinite-value~"); qheader(unset_var) |= SYM_SPECIAL_VAR; qvalue(unset_var) = unset_var; Lunintern(nil, unset_var); /* * Now in some minor sense the world is in a self-consistent state */ lisp_true = make_undefined_symbol("t"); qheader(lisp_true) |= SYM_SPECIAL_VAR; qvalue(lisp_true) = lisp_true; savedef = make_undefined_symbol("*savedef"); comma_symbol = make_undefined_symbol("~comma"); comma_at_symbol = make_undefined_symbol("~comma-at"); lambda = make_undefined_symbol("lambda"); funarg = make_undefined_symbol("funarg"); cfunarg = make_undefined_symbol("cfunarg"); opt_key = make_undefined_symbol("&optional"); rest_key = make_undefined_symbol("&rest"); #ifdef COMMON key_key = make_undefined_symbol("&key"); allow_other_keys = make_undefined_symbol("&allow-other-keys"); aux_key = make_undefined_symbol("&aux"); #endif work_symbol = make_undefined_symbol("~magic-internal-symbol~"); Lunintern(nil, work_symbol); #ifndef COMMON packid_(CP) = make_undefined_symbol("package"); #else package_symbol = make_undefined_symbol("package"); packid_(CP) = package_symbol; #endif macroexpand_hook = make_undefined_symbol("*macroexpand-hook*"); qheader(macroexpand_hook) |= SYM_SPECIAL_VAR; evalhook = make_undefined_symbol("*evalhook*"); qheader(evalhook) |= SYM_SPECIAL_VAR; qvalue(evalhook) = nil; applyhook = make_undefined_symbol("*applyhook*"); qheader(applyhook) |= SYM_SPECIAL_VAR; qvalue(applyhook) = nil; #ifdef COMMON keyword_package = make_undefined_symbol("*keyword-package*"); qheader(keyword_package) |= SYM_SPECIAL_VAR; qvalue(keyword_package) = make_package(make_string("KEYWORD")); err_table = make_undefined_symbol("*ERROR-MESSAGE*"); #else err_table = make_undefined_symbol("*error-messages*"); #endif qheader(err_table) |= SYM_SPECIAL_VAR; qvalue(err_table) = nil; #ifdef COMMON #define make_keyword(name) \ Lintern_2(nil, make_string(name), qvalue(keyword_package)) internal_symbol = make_keyword("INTERNAL"); external_symbol = make_keyword("EXTERNAL"); inherited_symbol = make_keyword("INHERITED"); allow_key_key = make_keyword("ALLOW-OTHER-KEYS"); #else #define make_keyword(name) make_undefined_symbol(name) #endif gensym_base = make_string("G"); #ifdef COMMON special_symbol = make_undefined_symbol("special"); expand_def_symbol = make_undefined_symbol("expand-definer"); format_symbol = make_undefined_symbol("format"); string_char_sym = make_undefined_symbol("string-char"); cl_symbols = make_undefined_symbol("*cl-symbols*"); /* * cl_symbols has to be at least a vector or else I can not * read in the Lisp file that sets its proper value... */ qvalue(cl_symbols) = getvector_init(8*CELL, nil); features_symbol = make_undefined_symbol("*features*"); qheader(cl_symbols) |= SYM_SPECIAL_VAR; qheader(features_symbol) |= SYM_SPECIAL_VAR; { Lisp_Object w; #define make_constant(name, value) \ w = make_undefined_symbol(name); \ qheader(w) |= SYM_SPECIAL_VAR; \ qvalue(w) = value; make_constant("most-positive-fixnum", fixnum_of_int(0x07ffffff)); make_constant("most-negative-fixnum", fixnum_of_int(0xf8000000)); /* #undef TYPE_LONG_FLOAT */ /* #define TYPE_LONG_FLOAT TYPE_DOUBLE_FLOAT */ make_constant("pi", make_boxfloat(3.141592653589793238, TYPE_LONG_FLOAT)); } #endif append_symbol = make_undefined_symbol("append"); raise_symbol = make_undefined_symbol("*raise"); lower_symbol = make_undefined_symbol("*lower"); echo_symbol = make_undefined_symbol("*echo"); /* * I think that having a built-in symbol called *hankaku even if Kanji support * is not otherwise present is not too severe a problem, and making the * symbol present always will help keep image files re-usable from one * version of CSL to another. */ hankaku_symbol = make_undefined_symbol("*hankaku"); comp_symbol = make_undefined_symbol("*comp"); compiler_symbol = make_undefined_symbol("compile"); native_symbol = make_undefined_symbol("native-compile"); bytecoded_symbol = make_undefined_symbol("bytecoded-definition"); nativecoded_symbol = make_undefined_symbol("native-code-definition"); traceprint_symbol = make_undefined_symbol("trace-print"); loadsource_symbol = make_symbol("load-source", 0, Lload_source, too_many_1, wrong_no_1); prinl_symbol = make_symbol("prinl", 0, Lprin, too_many_1, wrong_no_1); emsg_star = make_undefined_symbol("emsg*"); redef_msg = make_undefined_symbol("*redefmsg"); expr_symbol = make_undefined_symbol("expr"); fexpr_symbol = make_undefined_symbol("fexpr"); macro_symbol = make_undefined_symbol("macro"); break_function = make_undefined_symbol("*break-loop*"); gchook = make_undefined_symbol("*gc-hook*"); qheader(raise_symbol) |= SYM_SPECIAL_VAR; qheader(lower_symbol) |= SYM_SPECIAL_VAR; qheader(echo_symbol) |= SYM_SPECIAL_VAR; qheader(hankaku_symbol) |= SYM_SPECIAL_VAR; qheader(comp_symbol) |= SYM_SPECIAL_VAR; qheader(emsg_star) |= SYM_SPECIAL_VAR; qheader(redef_msg) |= SYM_SPECIAL_VAR; qheader(break_function) |= SYM_SPECIAL_VAR; qvalue(break_function) = nil; qheader(loadsource_symbol) |= SYM_SPECIAL_VAR; qvalue(loadsource_symbol) = nil; { Lisp_Object common = make_undefined_symbol("common-lisp-mode"); qheader(common) |= SYM_SPECIAL_VAR; #ifdef COMMON qvalue(common) = lisp_true; qvalue(raise_symbol) = lisp_true; qvalue(lower_symbol) = nil; #else qvalue(common) = nil; qvalue(raise_symbol) = nil; qvalue(lower_symbol) = lisp_true; #endif } qvalue(echo_symbol) = nil; qvalue(hankaku_symbol) = nil; qvalue(comp_symbol) = nil; qvalue(emsg_star) = nil; qvalue(redef_msg) = lisp_true; sys_hash_table = Lmkhash(nil, 3, fixnum_of_int(5), fixnum_of_int(2), nil); get_counts = Lmkhash(nil, 3, fixnum_of_int(5), fixnum_of_int(0), nil); /* * I make the vector that can hold the names used for "fast" get tags big * enough for the largest possible number. */ fastget_names = getvector_init((MAX_FASTGET_SIZE+2)*CELL, SPID_NOPROP); /* * The next bit is a horrid fudge, used in read.c (function orderp) to * support REDUCE. It ensures that the flag 'noncom is subject to an * optimisation for flag/flagp that allows it to be tested for using a * simple bit-test. This MUST use entry zero (coded as 1 here!). * Also I insist that 'lose be the second fastget thing! */ { Lisp_Object nc = make_undefined_symbol("noncom"); qheader(nc) |= (1L << SYM_FASTGET_SHIFT); elt(fastget_names, 0) = nc; nc = make_undefined_symbol("lose"); qheader(nc) |= (2L << SYM_FASTGET_SHIFT); elt(fastget_names, 1) = nc; } /* * I create the stream objects just once at cold-start time, but every time I * restart I will fill in their components in the standard way again. */ lisp_work_stream = make_stream_handle(); lisp_terminal_io = make_stream_handle(); lisp_standard_output = make_stream_handle(); lisp_standard_input = make_stream_handle(); lisp_error_output = make_stream_handle(); lisp_trace_output = make_stream_handle(); lisp_debug_io = make_stream_handle(); lisp_query_io = make_stream_handle(); inject_randomness((int)clock()); set_up_functions(0); set_up_variables(0, isdemo); } void set_up_functions(CSLbool restartp) { /* * All symbols that have a pointer to C code in their function cell must * be set up whether we are in a warm OR a cold start state, because the * actual addresses associated with C entrypoints will vary from version * to version of the binary of the system. */ int i; nil_as_base #ifdef COMMON /* * In Common Lisp mode it could be that the user had something other than the * LISP package active when the image was saved. But I want all the symbols * that I create or restore here to be in the LISP (or sometimes keyword) * package. So I temporarily reset the package here... */ Lisp_Object saved_package = CP; CP = find_package("LISP", 4); #endif function_symbol = make_symbol("function", restartp, function_fn, bad_special2, bad_specialn); qheader(function_symbol)|= SYM_SPECIAL_FORM; quote_symbol = make_symbol("quote", restartp, quote_fn, bad_special2, bad_specialn); qheader(quote_symbol) |= SYM_SPECIAL_FORM; progn_symbol = make_symbol("progn", restartp, progn_fn, bad_special2, bad_specialn); qheader(progn_symbol) |= SYM_SPECIAL_FORM; #ifdef COMMON declare_symbol = make_symbol("declare", restartp, declare_fn, bad_special2, bad_specialn); qheader(declare_symbol) |= SYM_SPECIAL_FORM; #endif cons_symbol = make_symbol("cons", restartp, too_few_2, Lcons, wrong_no_2); eval_symbol = make_symbol("eval", restartp, Leval, too_many_1, wrong_no_1); loadsource_symbol = make_symbol("load-source", restartp, Lload_source, too_many_1, wrong_no_1); /* * The main bunch of symbols can be handed using a table that * gives names and values. */ for (i=0; eval2_setup[i].name != NULL; i++) qheader(make_symbol(eval2_setup[i].name, restartp, eval2_setup[i].one, eval2_setup[i].two, eval2_setup[i].n)) |= SYM_SPECIAL_FORM; for (i=0; eval3_setup[i].name != NULL; i++) qheader(make_symbol(eval3_setup[i].name, restartp, eval3_setup[i].one, eval3_setup[i].two, eval3_setup[i].n)) |= SYM_SPECIAL_FORM; create_symbols(arith06_setup, restartp); create_symbols(arith08_setup, restartp); create_symbols(arith10_setup, restartp); create_symbols(arith12_setup, restartp); create_symbols(char_setup, restartp); create_symbols(eval1_setup, restartp); create_symbols(funcs1_setup, restartp); create_symbols(funcs2_setup, restartp); create_symbols(funcs3_setup, restartp); create_symbols(print_setup, restartp); create_symbols(read_setup, restartp); create_symbols(restart_setup, restartp); create_symbols(mpi_setup, restartp); /* * Although almost everything is mappeed into upper case in a Common Lisp * world I will preserve the case of symbols defined un u01 to u12. */ create_symbols(u01_setup, restartp | 2); create_symbols(u02_setup, restartp | 2); create_symbols(u03_setup, restartp | 2); create_symbols(u04_setup, restartp | 2); create_symbols(u05_setup, restartp | 2); create_symbols(u06_setup, restartp | 2); create_symbols(u07_setup, restartp | 2); create_symbols(u08_setup, restartp | 2); create_symbols(u09_setup, restartp | 2); create_symbols(u10_setup, restartp | 2); create_symbols(u11_setup, restartp | 2); create_symbols(u12_setup, restartp | 2); #ifdef NAG create_symbols(asp_setup, restartp); create_symbols(nag_setup, restartp); create_symbols(socket_setup, restartp); create_symbols(xdr_setup, restartp); create_symbols(grep_setup, restartp); create_symbols(axfns_setup, restartp); create_symbols(gr_setup, restartp); #endif #ifdef OPENMATH create_symbols(om_setup, restartp); create_symbols(om_parse_setup, restartp); #endif #ifdef MEMORY_TRACE #ifndef CHECK_ONLY memory_comment(13); /* tail end of setup */ #endif #endif #ifdef COMMON CP = saved_package; #endif } #ifndef COMMON #ifdef HAVE_FWIN static int MS_CDECL alpha1(const void *a, const void *b) { return strcmp(1+*(const char **)a, 1+*(const char **)b); } #else static int MS_CDECL alpha0(const void *a, const void *b) { return strcmp(*(const char **)a, *(const char **)b); } #endif #endif static void set_up_variables(CSLbool restartp, int isdemo) { Lisp_Object nil = C_nil; int i; #ifdef COMMON Lisp_Object saved_package = CP; CP = find_package("LISP", 4); #endif qvalue(macroexpand_hook) = make_symbol("funcall", restartp, Lfuncall1, Lfuncall2, Lfuncalln); input_libraries = make_undefined_symbol("input-libraries"); qheader(input_libraries) |= SYM_SPECIAL_FORM; qvalue(input_libraries) = nil; for (i=number_of_fasl_paths-1; i>=0; i--) qvalue(input_libraries) = cons(SPID_LIBRARY + (((int32_t)i)<<20), qvalue(input_libraries)); output_library = make_undefined_symbol("output-library"); qheader(output_library) |= SYM_SPECIAL_FORM; qvalue(output_library) = output_directory < 0 ? nil : SPID_LIBRARY + (((int32_t)output_directory)<<20); /* * The Lisp variable lispsystem* gets set here. (in COMMON mode it is * the variable *features*) * Its value is a list. * csl says I am a CSL Lisp * (executable . "string") name of current executable (if available) * (shortname . "string") executable wuithout path or extension * pipes do I support open-pipe? * (version . "string") eg "2.11" * (name . "string") eg "MSDOS/386" * (opsys . id) unix/msdos/riscos/win32/finder/riscos/... * id unix/msdos etc again... * help help mechanism provided within Lisp * debug Lisp built with debug options * (native . number) native code tag * (c-code . number) u01.c through u12.c define n functions * sixty-four 64-bit address version * texmacs "--texmacs" option on command line * demo the demo system * * In COMMON mode the tags on the *features* list are generally in the * keyword package. Otherwise they are just regular symbols. This makes it * slightly hard to use code that tests this list in a generic environment! */ { #ifdef COMMON Lisp_Object n = features_symbol; Lisp_Object w; char opsys[32]; char *p1 = opsys, *p2 = OPSYS; int ii; while ((*p1++ = toupper(*p2++)) != 0); *p1 = 0; w = cons(make_keyword(opsys), nil); #ifdef WIN64 w = cons(make_keyword("WIN32"), w); #endif w = acons(make_keyword("LINKER"), make_undefined_symbol(linker_type), w); w1 = nil; for (ii=sizeof(compiler_command)/sizeof(compiler_command[0])-1; ii>=0; ii--) w1 = cons(make_undefined_symbol(compiler_command[ii]), w1); w = acons(make_keyword("COMPILER-COMMAND"), w1, w); #else Lisp_Object n = make_undefined_symbol("lispsystem*"); Lisp_Object w = cons(make_keyword(OPSYS), nil), w1; int ii; #ifdef WIN64 /* * In the WIN64 case I will ALSO tell the user than I am "win32". This is * a curious thing to do maybe, but is because historically win32 may have * been used as a "windows" test, and win64 is in general terms a * compatible extension so all win32 options ought still to be available. */ w = cons(make_keyword("win32"), w); #endif qheader(n) |= SYM_SPECIAL_VAR; w = acons(make_keyword("linker"), make_undefined_symbol(linker_type), w); w1 = nil; for (ii=sizeof(compiler_command)/sizeof(compiler_command[0])-1; ii>=0; ii--) w1 = cons(make_undefined_symbol(compiler_command[ii]), w1); w = acons(make_keyword("compiler-command"), w1, w); #endif defined_symbols = 0; count_symbols(u01_setup); count_symbols(u02_setup); count_symbols(u03_setup); count_symbols(u04_setup); count_symbols(u05_setup); count_symbols(u06_setup); count_symbols(u07_setup); count_symbols(u08_setup); count_symbols(u09_setup); count_symbols(u10_setup); count_symbols(u11_setup); count_symbols(u12_setup); #ifdef COMMON /* * A gratuitous misery here is the need to make COMMON words * upper case. */ w = acons(make_keyword("OPSYS"), make_undefined_symbol(OPSYS), w); w = acons(make_keyword("NATIVE"), fixnum_of_int(native_code_tag), w); w = acons(make_keyword("C-CODE"), fixnum_of_int(defined_symbols), w); if (SIXTY_FOUR_BIT) w = cons(make_keyword("SIXTY-FOUR"), w); #if defined HAVE_POPEN || defined HAVE_FWIN w = cons(make_keyword("PIPES"), w); #endif #ifdef DEBUG w = cons(make_keyword("DEBUG"), w); #endif w = cons(make_keyword("RECORD_GET"), w); #ifdef HAVE_FWIN w = acons(make_keyword("EXECUTABLE"), make_string(fwin_full_program_name), w); #else if (program_name[0] != 0) w = acons(make_keyword("EXECUTABLE"), make_string(program_name), w); #endif w = acons(make_keyword("NAME"), make_string(IMPNAME), w); w = acons(make_keyword("VERSION"), make_string(VERSION), w); w = cons(make_keyword("CCL"), w); w = cons(make_keyword("COMMON-LISP"), w); if (isdemo) w = cons(make_keyword("DEMO"), w); #else /* !COMMON */ w = acons(make_keyword("opsys"), make_undefined_symbol(OPSYS), w); w = acons(make_keyword("native"), fixnum_of_int(native_code_tag), w); w = acons(make_keyword("c-code"), fixnum_of_int(defined_symbols), w); #ifdef HAVE_FWIN if (texmacs_mode) w = cons(make_keyword("texmacs"), w); #endif if (SIXTY_FOUR_BIT) w = cons(make_keyword("sixty-four"), w); #if defined HAVE_POPEN || defined HAVE_FWIN w = cons(make_keyword("pipes"), w); #endif #ifdef DEBUG w = cons(make_keyword("debug"), w); #endif #ifdef HAVE_FWIN if (fwin_windowmode() & FWIN_WITH_TERMED) w = cons(make_keyword("termed"), w); #ifdef HAVE_LIBFOX // if (fwin_windowmode() & FWIN_WITH_FOX) REINSTATE SOON PLEASE // w = cons(make_keyword("fox"), w); if (fwin_windowmode() & FWIN_IN_WINDOW) { w = cons(make_keyword("windowed"), w); // It could be the case that SHOWMATH is compiled in but the necessary // fonts were not located. Or if they were there but "--" has been used to // redirect standard output to a file. if (showmathInitialised && alternative_stdout == NULL) w = cons(make_keyword("showmath"), w); } #endif #endif #ifdef RECORD_GET w = cons(make_keyword("record_get"), w); #endif #ifdef HAVE_FWIN w = acons(make_keyword("executable"), make_string(fwin_full_program_name), w); w = acons(make_keyword("shortname"), make_string(programName), w); #else if (program_name[0] != 0) w = acons(make_keyword("executable"), make_string(program_name), w); #endif w = acons(make_keyword("name"), make_string(IMPNAME), w); w = acons(make_keyword("version"), make_string(VERSION), w); w = cons(make_keyword("csl"), w); if (isdemo) w = cons(make_keyword("demo"), w); /* * Ha Ha a trick here - if a symbol ADDSQ is defined I view this image * as being one for REDUCE and push that information onto lispsystem*, * and I also reset the "about box" information (if using fwin). */ w1 = make_undefined_symbol("addsq"); if (qfn1(w1) != undefined1) { w = cons(make_keyword("reduce"), w); /* * I then inspect VERSION!* to try to see whether I have 3.6, 3.7, 3.8, ... */ w1 = qvalue(make_undefined_symbol("version*")); if (is_vector(w1) && type_of_header(vechdr(w1)) == TYPE_STRING) { #ifdef HAVE_FWIN int n = length_of_header(vechdr(w1))-CELL; sprintf(about_box_title, "About %.*s", (n > 31-(int)strlen("About ") ? 31-(int)strlen("About ") : n), &celt(w1, 0)); sprintf(about_box_description, "%.*s", (n > 31 ? 31 : n), &celt(w1, 0)); w1 = qvalue(make_undefined_symbol("copyright1*")); if (is_vector(w1) && type_of_header(vechdr(w1)) == TYPE_STRING) { n = length_of_header(vechdr(w1))-CELL; sprintf(about_box_rights_1, "%.*s", n > 31 ? 31 : n, &celt(w1, 0)); } else strcpy(about_box_rights_1, "Copyright A C Hearn/RAND"); w1 = qvalue(make_undefined_symbol("copyright2*")); if (is_vector(w1) && type_of_header(vechdr(w1)) == TYPE_STRING) { n = length_of_header(vechdr(w1))-CELL; sprintf(about_box_rights_2, "%.*s", n > 31 ? 31 : n, &celt(w1, 0)); } else strcpy(about_box_rights_2, "Copyright Codemist Ltd"); #endif } else { #ifdef HAVE_FWIN strcpy(about_box_title, "About REDUCE"); strcpy(about_box_description, "REDUCE"); strcpy(about_box_rights_1, "Copyright A C Hearn/RAND"); strcpy(about_box_rights_2, "Copyright Codemist Ltd"); #endif } } #endif qheader(n) |= SYM_SPECIAL_VAR; qvalue(n) = w; } #ifdef COMMON /* * Floating point characteristics are taken from <float.h> where it is * supposed that the C compiler involved has got the values correct. * I do this every time the system is loaded rather than just when an * image is cold-created. This is because an image file may have been created * on a system differing from the one on which it is used. Mayve in fact * IEEE arithmetic is ALMOST universal and I am being too cautious here? */ { Lisp_Object w; make_constant("short-float-epsilon", make_sfloat(16.0*FLT_EPSILON)); make_constant("single-float-epsilon", make_boxfloat(FLT_EPSILON, TYPE_SINGLE_FLOAT)); make_constant("double-float-epsilon", make_boxfloat(DBL_EPSILON, TYPE_DOUBLE_FLOAT)); /* For now "long" = "double" */ make_constant("long-float-epsilon", make_boxfloat(DBL_EPSILON, TYPE_LONG_FLOAT)); /* * I assume that I have a radix 2 representation, and float-negative-epsilon * is just half float-epsilon. Correct me if I am wrong... */ make_constant("short-float-negative-epsilon", make_sfloat(16.0*FLT_EPSILON/2.0)); make_constant("single-float-negative-epsilon", make_boxfloat(FLT_EPSILON/2.0, TYPE_SINGLE_FLOAT)); make_constant("double-float-negative-epsilon", make_boxfloat(DBL_EPSILON/2.0, TYPE_DOUBLE_FLOAT)); /* For now "long" = "double" */ make_constant("long-float-negative-epsilon", make_boxfloat(DBL_EPSILON/2.0, TYPE_LONG_FLOAT)); /* * I hope that the C header file gets extremal values correct. Note that * because make_sfloat() truncates (rather than rounding) it should give * correct values for most-positive-short-float etc */ make_constant("most-positive-short-float", make_sfloat(FLT_MAX)); make_constant("most-positive-single-float", make_boxfloat(FLT_MAX, TYPE_SINGLE_FLOAT)); make_constant("most-positive-double-float", make_boxfloat(DBL_MAX, TYPE_DOUBLE_FLOAT)); make_constant("most-positive-long-float", make_boxfloat(DBL_MAX, TYPE_LONG_FLOAT)); /* * Here I assume that the floating point representation is sign-and-magnitude * and hence symmetric about zero. */ make_constant("most-negative-short-float", make_sfloat(-FLT_MAX)); make_constant("most-negative-single-float", make_boxfloat(-FLT_MAX, TYPE_SINGLE_FLOAT)); make_constant("most-negative-double-float", make_boxfloat(-DBL_MAX, TYPE_DOUBLE_FLOAT)); make_constant("most-negative-long-float", make_boxfloat(-DBL_MAX, TYPE_LONG_FLOAT)); /* * The "least-xxx" set of values did not consider the case of denormalised * numbers too carefully in ClTl-1, so in ClTl-2 there are elaborations. I * believe that a proper C header file <float.h> will make the macros that * I use here refer to NORMALISED values, so the numeric results I use * here will not be quite proper (ie there are smaller floats that are * un-normalised). But I will ignore that worry just for now. */ make_constant("least-positive-short-float", make_sfloat(FLT_MIN)); make_constant("least-positive-single-float", make_boxfloat(FLT_MIN, TYPE_SINGLE_FLOAT)); make_constant("least-positive-double-float", make_boxfloat(DBL_MIN, TYPE_DOUBLE_FLOAT)); make_constant("least-positive-long-float", make_boxfloat(DBL_MIN, TYPE_LONG_FLOAT)); make_constant("least-negative-short-float", make_sfloat(-FLT_MIN)); make_constant("least-negative-single-float", make_boxfloat(-FLT_MIN, TYPE_SINGLE_FLOAT)); make_constant("least-negative-double-float", make_boxfloat(-DBL_MIN, TYPE_DOUBLE_FLOAT)); make_constant("least-negative-long-float", make_boxfloat(-DBL_MIN, TYPE_LONG_FLOAT)); /* * The bunch here are intended to be NORMALISED numbers, while the unqualified * ones above may not be. */ make_constant("least-positive-normalized-short-float", make_sfloat(FLT_MIN)); make_constant("least-positive-normalized-single-float", make_boxfloat(FLT_MIN, TYPE_SINGLE_FLOAT)); make_constant("least-positive-normalized-double-float", make_boxfloat(DBL_MIN, TYPE_DOUBLE_FLOAT)); make_constant("least-positive-normalized-long-float", make_boxfloat(DBL_MIN, TYPE_LONG_FLOAT)); make_constant("least-negative-normalized-short-float", make_sfloat(-FLT_MIN)); make_constant("least-negative-normalized-single-float", make_boxfloat(-FLT_MIN, TYPE_SINGLE_FLOAT)); make_constant("least-negative-normalized-double-float", make_boxfloat(-DBL_MIN, TYPE_DOUBLE_FLOAT)); make_constant("least-negative-normalized-long-float", make_boxfloat(-DBL_MIN, TYPE_LONG_FLOAT)); #endif #ifdef UNIX_TIMES /* /* * ACN believes that the following is misguided, since the time-reading * function (defined in fns1.c) that CCL provides always returns its answer * in milliseconds. This the 1000 below is NOT as arbitrary as all that, it * represents the unit that CCL (across all platforms) returns time * measurements in. The UNIX_TIMES macro is set on Unix systems to * influence whether the times() function or clock() is used to read * time, where in the former case Unix makes it possible to separate * user and system time. */ /* UNIX_TIMES is set in machine.h and will usually be HZ. */ make_constant("internal-time-units-per-second", #ifdef UNIX_TIMES fixnum_of_int(UNIX_TIMES)); #else fixnum_of_int(1000)); #endif } #endif #ifdef MEMORY_TRACE #ifndef CHECK_ONLY memory_comment(3); /* creating symbols */ #endif #endif charvec = getvector_init(257*CELL, nil); faslvec = nil; faslgensyms = nil; qheader(terminal_io = make_undefined_symbol("*terminal-io*")) |= SYM_SPECIAL_VAR; qheader(standard_input = make_undefined_symbol("*standard-input*")) |= SYM_SPECIAL_VAR; qheader(standard_output = make_undefined_symbol("*standard-output*")) |= SYM_SPECIAL_VAR; qheader(error_output = make_undefined_symbol("*error-output*")) |= SYM_SPECIAL_VAR; qheader(trace_output = make_undefined_symbol("*trace-output*")) |= SYM_SPECIAL_VAR; qheader(debug_io = make_undefined_symbol("*debug-io*")) |= SYM_SPECIAL_VAR; qheader(query_io = make_undefined_symbol("*query-io*")) |= SYM_SPECIAL_VAR; stream_type(lisp_work_stream) = make_undefined_symbol("work-stream"); { Lisp_Object f = lisp_terminal_io; stream_type(f) = make_undefined_symbol("terminal-stream"); set_stream_read_fn(f, char_from_terminal); set_stream_read_other(f, read_action_terminal); set_stream_write_fn(f, char_to_terminal); set_stream_write_other(f, write_action_terminal); qvalue(terminal_io) = f; f = lisp_standard_input; stream_type(f) = make_undefined_symbol("synonym-stream"); #ifdef COMMON /* * If I do not have COMMON defined I will take a slight short cut here and * make reading from *standard-input* read directly from the terminal. For * full Common Lisp compatibility I think *standard-input* is required to * be a synonym stream that will dynamically look at the value of the variable * *terminal-io* every time it does anything. Ugh, since people who assign to * or re-bind *terminal-io* seem to me to be asking for terrible trouble! */ set_stream_read_fn(f, char_from_synonym); #else set_stream_read_fn(f, char_from_terminal); #endif set_stream_read_other(f, read_action_synonym); stream_read_data(f) = terminal_io; qvalue(standard_input) = f; f = lisp_standard_output; stream_type(f) = make_undefined_symbol("synonym-stream"); #ifdef COMMON set_stream_write_fn(f, char_to_synonym); #else set_stream_write_fn(f, char_to_terminal); #endif set_stream_write_other(f, write_action_synonym); stream_write_data(f) = terminal_io; qvalue(standard_output) = f; f = lisp_error_output; stream_type(f) = make_undefined_symbol("synonym-stream"); #ifdef COMMON set_stream_write_fn(f, char_to_synonym); #else set_stream_write_fn(f, char_to_terminal); #endif set_stream_write_other(f, write_action_synonym); stream_write_data(f) = terminal_io; qvalue(error_output) = f; f = lisp_trace_output; stream_type(f) = make_undefined_symbol("synonym-stream"); #ifdef COMMON set_stream_write_fn(f, char_to_synonym); #else set_stream_write_fn(f, char_to_terminal); #endif set_stream_write_other(f, write_action_synonym); stream_write_data(f) = terminal_io; qvalue(trace_output) = f; f = lisp_debug_io; stream_type(f) = make_undefined_symbol("synonym-stream"); #ifdef COMMON set_stream_read_fn(f, char_from_synonym); #else set_stream_read_fn(f, char_from_terminal); #endif set_stream_read_other(f, read_action_synonym); stream_read_data(f) = terminal_io; #ifdef COMMON set_stream_write_fn(f, char_to_synonym); #else set_stream_write_fn(f, char_to_terminal); #endif set_stream_write_other(f, write_action_synonym); stream_write_data(f) = terminal_io; qvalue(debug_io) = f; f = lisp_query_io; stream_type(f) = make_undefined_symbol("synonym-stream"); #ifdef COMMON set_stream_read_fn(f, char_from_synonym); #else set_stream_read_fn(f, char_from_terminal); #endif set_stream_read_other(f, read_action_synonym); stream_read_data(f) = terminal_io; #ifdef COMMON set_stream_write_fn(f, char_to_synonym); #else set_stream_write_fn(f, char_to_terminal); #endif set_stream_write_other(f, write_action_synonym); stream_write_data(f) = terminal_io; qvalue(query_io) = f; } #ifdef HAVE_LIBFOX { Lisp_Object stream = make_undefined_symbol("*math-output*"); Lisp_Object f = make_stream_handle(); qheader(stream) |= SYM_SPECIAL_VAR; stream_type(f) = make_undefined_symbol("math-output"); set_stream_write_fn(f, char_to_math); set_stream_write_other(f, write_action_math); qvalue(stream) = f; stream = make_undefined_symbol("*spool-output*"); qheader(stream) |= SYM_SPECIAL_VAR; f = make_stream_handle(); stream_type(f) = make_undefined_symbol("spool-output"); set_stream_write_fn(f, char_to_spool); set_stream_write_other(f, write_action_spool); qvalue(stream) = f; } #endif /* * I can not handle boffo overflow very well here, but I do really hope that * symbols spelt out on the command line will always be fairly short. */ for (i=0; i<number_of_symbols_to_define; i++) { CSLbool undef = undefine_this_one[i]; char *s = symbols_to_define[i]; if (undef) { Lisp_Object n = make_undefined_symbol(s); qvalue(n) = unset_var; } else { char buffer[256]; char *p = buffer; int c; Lisp_Object n, v; while ((c = *s++) != 0 && c != '=') *p++ = (char)c; *p = 0; n = make_undefined_symbol(buffer); push(n); if (c == 0) v = lisp_true; else { /* * I have been having a big difficulty here, caused by the inconsistent and * awkward behaviours of various shells and "make" utilities. In a tidy * and simple world I might like a command-line option -Dxx=yyy to allow * arbitrary text for yyy terminating it at the next whitespace. Then yyy * could be processed by the Lisp reader so that numbers, symbols, strings * etc could be specified. However I find that things I often want to * use involve characters such as "\" and ":" (as components of file-names * on some machines), and sometimes "make" treats these as terminators, or * wants to do something magic with "\". If I put things within quote marks * then sometimes the quotes get passed through to Lisp and sometimes not. * This is all a BIG misery in a multi-platform situation! As a fresh * attempt to inject sanity I will always convert yyy to a Lisp string. If * it is specified with leading and trailing '"' marks I will strip them. Thus * both -Dxxx=yyy and -Dxxx="yyy" will leave the variable xxx set to the * string "yyy". Then as a Lisp user I can parse the string if I need to * interpret it as something else. */ #ifndef PASS_PREDEFINES_THROUGH_READER if (*s == '"') /* Convert "yyy" to just yyy */ { p = ++s; while (*p != 0) p++; if (*--p == '"') *p = 0; } #endif v = make_string(s); #ifdef PASS_PREDEFINES_THROUGH_READER v = Lexplodec(nil, v); v = Lcompress(nil, v); /* * The above will first make the value in -Dname=value into a string, * then explode it into a list, and compress back - the effect is as if the * original value had been passed through the regular Lisp READ function, * so symbols, numbers and even s-expressions can be parsed. If the * parsing fails I (silently) treat the value as just NIL. */ #endif nil = C_nil; if (exception_pending()) v = flip_exception(); } pop(n); qheader(n) |= SYM_SPECIAL_VAR; qvalue(n) = v; } } #ifndef COMMON #ifdef HAVE_FWIN /* * Now if I have the FWIN windowed system I look in the Lisp variables * loadable-packages!* * switches!* * (both expected to be lists of symbols) and copy info into a couple of * C vectors, whence it can go to the window manager and be used to create * suitable menus. I might get in a mess if I try to set and reset menus * multiple times, and so to avoid possible confusion I do this step * JUST ONCE. This may be limiting (in particular it means that menus get * set at the very start of a run ONLY) but should only be visible to those * who call restart!-csl. */ if (loadable_packages == NULL && switches==NULL) { Lisp_Object w1 = qvalue(make_undefined_symbol("loadable-packages*")); Lisp_Object w2; int n; char *v; n = 0; for (w2=w1; consp(w2); w2=qcdr(w2)) n++; /* How many? */ #ifdef HAVE_FWIN n = 2*n; #endif loadable_packages = (char **)(*malloc_hook)((n+1)*sizeof(char *)); if (loadable_packages != NULL) { n = 0; for (w2=w1; consp(w2); w2=qcdr(w2)) { Lisp_Object w3 = qcar(w2); int n1; if (is_symbol(w3)) w3 = qpname(w3); if (!is_vector(w3) || type_of_header(vechdr(w3)) != TYPE_STRING) break; n1 = length_of_header(vechdr(w3))-CELL; #ifdef HAVE_FWIN v = (char *)(*malloc_hook)(n1+2); if (v == NULL) break; v[0] = ' '; memcpy(v+1, &celt(w3, 0), n1); v[n1+1] = 0; #else v = (char *)(*malloc_hook)(n1+1); if (v == NULL) break; memcpy(v, &celt(w3, 0), n1); v[n1] = 0; #endif loadable_packages[n++] = v; #ifdef HAVE_FWIN loadable_packages[n++] = NULL; #endif } #ifdef HAVE_FWIN qsort(loadable_packages, n/2, 2*sizeof(char *), alpha1); #else qsort(loadable_packages, n, sizeof(char *), alpha0); #endif loadable_packages[n] = NULL; /* NULL-terminate the list */ } w1 = qvalue(make_undefined_symbol("switches*")); n = 0; for (w2=w1; consp(w2); w2=qcdr(w2)) n++; /* How many? */ n = (n+1)*sizeof(char *); #ifdef HAVE_FWIN n = 2*n; #endif switches = (char **)(*malloc_hook)(n); if (switches != NULL) { n = 0; for (w2=w1; consp(w2); w2=qcdr(w2)) { Lisp_Object w3 = qcar(w2), w4; char sname[64]; int n1; if (is_symbol(w3)) w3 = qpname(w3); if (!is_vector(w3) || type_of_header(vechdr(w3)) != TYPE_STRING) break; n1 = length_of_header(vechdr(w3))-CELL; if (n1 > 60) break; sprintf(sname, "*%.*s", n1, &celt(w3, 0)); w4 = make_undefined_symbol(sname); v = (char *)(*malloc_hook)(n1+2); if (v == NULL) break; /* * The first character records the current state of the switch. With FWIN * I have entries that say "x" for "I am not at present active" which copes * with switches that will become relevant only when a package of code is * loaded. I will scan from time to time to update my information - I guess * that I can put in a hook that triggers review after any module has been * loaded. See the function review_switch_settings() the follows... */ if (qvalue(w4) == nil) v[0] = 'n'; #ifdef HAVE_FWIN else if (qvalue(w4) == unset_var) v[0] = 'x'; #endif else v[0] = 'y'; memcpy(v+1, &celt(w3, 0), n1); v[n1+1] = 0; switches[n++] = v; #ifdef HAVE_FWIN switches[n++] = NULL; #endif } #ifdef HAVE_FWIN qsort(switches, n/2, 2*sizeof(char *), alpha1); #else qsort(switches, n, sizeof(char *), alpha1); #endif switches[n] = NULL; } } #endif /* HAVE_FWIN */ #endif /* COMMON */ #ifdef COMMON CP = saved_package; #endif } #ifndef COMMON #ifdef HAVE_FWIN /* * This alse reviews the list of loaded packages... */ void review_switch_settings() { Lisp_Object sw = qvalue(make_undefined_symbol("switches*")); while (consp(sw)) { Lisp_Object s = qcar(sw); char sname[64]; int n1; char *v, **p; Lisp_Object nil, starsw; sw = qcdr(sw); if (is_symbol(s)) s = qpname(s); if (!is_vector(s) || type_of_header(vechdr(s)) != TYPE_STRING) continue; n1 = length_of_header(vechdr(s))-CELL; if (n1 > 60) continue; sprintf(sname, "*%.*s", n1, &celt(s, 0)); for (p=switches; *p!=NULL; p+=2) { if (strcmp(1+*p, &sname[1]) == 0) break; } if ((v=*p) == NULL) continue; starsw = make_undefined_symbol(sname); nil = C_nil; if (exception_pending()) { flip_exception(); continue; } if (qvalue(starsw) == nil) switch(*v) { case 'y': *v = 0x3f&'N'; break; case 'n': break; case 'x': *v = 'N'; break; } else if (qvalue(starsw) == unset_var) switch(*v) { case 'y': *v = 'X'; break; case 'n': *v = 'X'; break; case 'x': break; } else switch(*v) { case 'y': break; case 'n': *v = 0x3f&'Y'; break; case 'x': *v = 'Y'; break; } } sw = qvalue(make_undefined_symbol("loaded-packages*")); while (consp(sw)) { Lisp_Object s = qcar(sw); char sname[64]; int n1; char *v, **p; sw = qcdr(sw); if (is_symbol(s)) s = qpname(s); if (!is_vector(s) || type_of_header(vechdr(s)) != TYPE_STRING) continue; n1 = length_of_header(vechdr(s))-CELL; if (n1 > 60) continue; sprintf(sname, "%.*s", n1, &celt(s, 0)); for (p=loadable_packages; *p!=NULL; p+=2) { if (strcmp(1+*p, sname) == 0) break; } if ((v=*p) == NULL) continue; if (*v == ' ') *v = 'X'; /* X here says "update the info" */ } fwin_refresh_switches(switches, loadable_packages); } #endif #endif unsigned char registration_data[REGISTRATION_SIZE]; CSLbool CSL_MD5_busy; unsigned char unpredictable[256]; static int n_unpredictable = 0; static CSLbool unpredictable_pending = 0; void inject_randomness(int n) { unpredictable[n_unpredictable++] ^= (n % 255); if (n_unpredictable >= 256) { n_unpredictable = 0; unpredictable_pending = YES; } if (unpredictable_pending & !CSL_MD5_busy) { CSL_MD5_Init(); CSL_MD5_Update(unpredictable, sizeof(unpredictable)); CSL_MD5_Final(unpredictable); unpredictable_pending = NO; } } /* * For some of what follows I think I need to show that I have considered * the issue of export regulations. * * What I have here is MD5 (and when and if I feel keen SHA-1). I observe * that MD5, SHA-1 and DSA are made available as part of Sun's Java * Development Kit in the version that can be downloade freely from their * servers. They have a separate Java Cryptography Extension within which * they keep things that are subject to USA export regulations. I take this * as encouragement to believe that these three algorithms are not subject * to USA export limits. I believe such limits to be supersets (ie more * restrictive) than ones that apply in the UK and so feel happy about * including the implementations that I do here. Specifically, although I * have extracts from the SSL code which as a whole might give trouble if * importen to the USA and the re-exported I only have the message digest * bits that should not be so encumbered. I am aware that MD5 is now * considered weakish with SHA-1 the improved replacement, but will take the * view that I was not aiming for real security on anything anyway! */ /* * MD5 message digest code, adapted from Eric Young's version, * for which the copyright and disclaimer notices follow. Observe that * this code can be adapted and re-used subject to these terms being * retained. * * NOTE that I have stuck "CSL_" on the front of names since in some cases * a crypto library may find itself getting linked in with bits of CSL code * and names could otehrwise clash. Specifically this could happen on * Mac/Darwin when CSL is built with a flat namespace ready for dynamically * loading modules. */ /* crypto/md/md5.c and support files */ /* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@mincom.oz.au). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@mincom.oz.au). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * "This product includes cryptographic software written by * Eric Young (eay@mincom.oz.au)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] */ /* * End of Eric Young's copyright and disclaimer notice. * * The changes made by A C Norman remove some optimisation to leave shorter * code (I will not be using this in speed-critical applications) and * adjusting the style and layout to agree with other Codemist utilities. */ #define MD5_CBLOCK 64 #define MD5_LBLOCK 16 static uint32_t MD5_A, MD5_B, MD5_C, MD5_D; static uint32_t MD5_Nl; static int MD5_num; static uint32_t MD5_data[MD5_CBLOCK]; #define F(x,y,z) ((((y) ^ (z)) & (x)) ^ (z)) #define G(x,y,z) ((((x) ^ (y)) & (z)) ^ (y)) #define H(x,y,z) ((x) ^ (y) ^ (z)) #define I(x,y,z) (((x) | (~(z))) ^ (y)) #define ROTATE(a,n) (((a)<<(n))|((a)>>(32-(n)))) #define R0(a,b,c,d,k,s,t) { \ a+=((k)+(t)+F((b),(c),(d))); \ a=ROTATE(a,s); \ a+=b; } #define R1(a,b,c,d,k,s,t) { \ a+=((k)+(t)+G((b),(c),(d))); \ a=ROTATE(a,s); \ a+=b; } #define R2(a,b,c,d,k,s,t) { \ a+=((k)+(t)+H((b),(c),(d))); \ a=ROTATE(a,s); \ a+=b; } #define R3(a,b,c,d,k,s,t) { \ a+=((k)+(t)+I((b),(c),(d))); \ a=ROTATE(a,s); \ a+=b; } /* * Implemented from RFC1321 The MD5 Message-Digest Algorithm */ void CSL_MD5_Init(void) { CSL_MD5_busy = YES; MD5_A = 0x67452301; MD5_B = 0xefcdab89; MD5_C = 0x98badcfe; MD5_D = 0x10325476; MD5_Nl = 0; MD5_num = 0; } static unsigned char byte_order_test[4] = {1, 0, 0, 0}; static void md5_block(void) { uint32_t A=MD5_A, B=MD5_B, C=MD5_C, D=MD5_D; int i; /* * Here I re-write the buffer so that it now behaves as if it is * an array of 32-bit words in native computer representation. On * many machines the code here will have no effect at all apart from * consuming a little time. I do a little test first to see if * it is really needed. */ uint32_t *p = MD5_data; unsigned char *q = (unsigned char *)p; if (((uint32_t *)byte_order_test)[0] != 1) { for (i=0; i<MD5_LBLOCK; i++) { uint32_t w = *q++; w |= *q++ << 8; w |= *q++ << 16; w |= *q++ << 24; *p++ = w; } } p = MD5_data; /* Round 0 */ R0(A,B,C,D,p[ 0], 7,0xd76aa478); R0(D,A,B,C,p[ 1],12,0xe8c7b756); R0(C,D,A,B,p[ 2],17,0x242070db); R0(B,C,D,A,p[ 3],22,0xc1bdceee); R0(A,B,C,D,p[ 4], 7,0xf57c0faf); R0(D,A,B,C,p[ 5],12,0x4787c62a); R0(C,D,A,B,p[ 6],17,0xa8304613); R0(B,C,D,A,p[ 7],22,0xfd469501); R0(A,B,C,D,p[ 8], 7,0x698098d8); R0(D,A,B,C,p[ 9],12,0x8b44f7af); R0(C,D,A,B,p[10],17,0xffff5bb1); R0(B,C,D,A,p[11],22,0x895cd7be); R0(A,B,C,D,p[12], 7,0x6b901122); R0(D,A,B,C,p[13],12,0xfd987193); R0(C,D,A,B,p[14],17,0xa679438e); R0(B,C,D,A,p[15],22,0x49b40821); /* Round 1 */ R1(A,B,C,D,p[ 1], 5,0xf61e2562); R1(D,A,B,C,p[ 6], 9,0xc040b340); R1(C,D,A,B,p[11],14,0x265e5a51); R1(B,C,D,A,p[ 0],20,0xe9b6c7aa); R1(A,B,C,D,p[ 5], 5,0xd62f105d); R1(D,A,B,C,p[10], 9,0x02441453); R1(C,D,A,B,p[15],14,0xd8a1e681); R1(B,C,D,A,p[ 4],20,0xe7d3fbc8); R1(A,B,C,D,p[ 9], 5,0x21e1cde6); R1(D,A,B,C,p[14], 9,0xc33707d6); R1(C,D,A,B,p[ 3],14,0xf4d50d87); R1(B,C,D,A,p[ 8],20,0x455a14ed); R1(A,B,C,D,p[13], 5,0xa9e3e905); R1(D,A,B,C,p[ 2], 9,0xfcefa3f8); R1(C,D,A,B,p[ 7],14,0x676f02d9); R1(B,C,D,A,p[12],20,0x8d2a4c8a); /* Round 2 */ R2(A,B,C,D,p[ 5], 4,0xfffa3942); R2(D,A,B,C,p[ 8],11,0x8771f681); R2(C,D,A,B,p[11],16,0x6d9d6122); R2(B,C,D,A,p[14],23,0xfde5380c); R2(A,B,C,D,p[ 1], 4,0xa4beea44); R2(D,A,B,C,p[ 4],11,0x4bdecfa9); R2(C,D,A,B,p[ 7],16,0xf6bb4b60); R2(B,C,D,A,p[10],23,0xbebfbc70); R2(A,B,C,D,p[13], 4,0x289b7ec6); R2(D,A,B,C,p[ 0],11,0xeaa127fa); R2(C,D,A,B,p[ 3],16,0xd4ef3085); R2(B,C,D,A,p[ 6],23,0x04881d05); R2(A,B,C,D,p[ 9], 4,0xd9d4d039); R2(D,A,B,C,p[12],11,0xe6db99e5); R2(C,D,A,B,p[15],16,0x1fa27cf8); R2(B,C,D,A,p[ 2],23,0xc4ac5665); /* Round 3 */ R3(A,B,C,D,p[ 0], 6,0xf4292244); R3(D,A,B,C,p[ 7],10,0x432aff97); R3(C,D,A,B,p[14],15,0xab9423a7); R3(B,C,D,A,p[ 5],21,0xfc93a039); R3(A,B,C,D,p[12], 6,0x655b59c3); R3(D,A,B,C,p[ 3],10,0x8f0ccc92); R3(C,D,A,B,p[10],15,0xffeff47d); R3(B,C,D,A,p[ 1],21,0x85845dd1); R3(A,B,C,D,p[ 8], 6,0x6fa87e4f); R3(D,A,B,C,p[15],10,0xfe2ce6e0); R3(C,D,A,B,p[ 6],15,0xa3014314); R3(B,C,D,A,p[13],21,0x4e0811a1); R3(A,B,C,D,p[ 4], 6,0xf7537e82); R3(D,A,B,C,p[11],10,0xbd3af235); R3(C,D,A,B,p[ 2],15,0x2ad7d2bb); R3(B,C,D,A,p[ 9],21,0xeb86d391); MD5_A += A; MD5_B += B; MD5_C += C; MD5_D += D; } void CSL_MD5_Update(unsigned char *data, int len) { unsigned char *p = (unsigned char *)MD5_data; /* * The full MD5 procedure allows for encoding strings of up to * around 2^64 bits. I will restrict myself to 2^32 so I can just ignore * the high word of the bit-count. */ MD5_Nl += len<<3; /* Counts in BITS not BYTES here */ while (len != 0) { p[MD5_num++] = *data++; len--; if (MD5_num == MD5_CBLOCK) { md5_block(); MD5_num = 0; } } } void CSL_MD5_Final(unsigned char *md) { uint32_t l = MD5_Nl; unsigned char *p = (unsigned char *)MD5_data; p[MD5_num++] = 0x80; if (MD5_num >= MD5_CBLOCK-8) { while (MD5_num < MD5_CBLOCK) p[MD5_num++] = 0; md5_block(); MD5_num = 0; } while (MD5_num < MD5_CBLOCK-8) p[MD5_num++] = 0; p[MD5_num++] = (unsigned char)l; p[MD5_num++] = (unsigned char)(l>>8); p[MD5_num++] = (unsigned char)(l>>16); p[MD5_num++] = (unsigned char)(l>>24); p[MD5_num++] = 0; p[MD5_num++] = 0; p[MD5_num++] = 0; p[MD5_num++] = 0; md5_block(); p = md; l = MD5_A; *p++ = (unsigned char)l; *p++ = (unsigned char)(l>>8); *p++ = (unsigned char)(l>>16); *p++ = (unsigned char)(l>>24); l = MD5_B; *p++ = (unsigned char)l; *p++ = (unsigned char)(l>>8); *p++ = (unsigned char)(l>>16); *p++ = (unsigned char)(l>>24); l = MD5_C; *p++ = (unsigned char)l; *p++ = (unsigned char)(l>>8); *p++ = (unsigned char)(l>>16); *p++ = (unsigned char)(l>>24); l = MD5_D; *p++ = (unsigned char)l; *p++ = (unsigned char)(l>>8); *p++ = (unsigned char)(l>>16); *p++ = (unsigned char)(l>>24); CSL_MD5_busy = NO; } unsigned char *CSL_MD5(unsigned char *d, int n, unsigned char *md) { if (n < 0) n = strlen((char *)d); CSL_MD5_Init(); CSL_MD5_Update(d, n); CSL_MD5_Final(md); return md; } #ifdef STAND_ALONE_TESTING_OF_MD5_CODE int main(int argc, char *argv[]) { int i; unsigned char mm[16]; CSL_MD5("", 0, mm); for (i=0; i<16; i++) printf("%.2x", mm[i] & 0xff); printf("\n"); CSL_MD5("a", 1, mm); for (i=0; i<16; i++) printf("%.2x", mm[i] & 0xff); printf("\n"); CSL_MD5("abc", 3, mm); for (i=0; i<16; i++) printf("%.2x", mm[i] & 0xff); printf("\n"); CSL_MD5("message digest", -1, mm); for (i=0; i<16; i++) printf("%.2x", mm[i] & 0xff); printf("\n"); CSL_MD5("abcdefghijklmnopqrstuvwxyz", -1, mm); for (i=0; i<16; i++) printf("%.2x", mm[i] & 0xff); printf("\n"); CSL_MD5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", -1, mm); for (i=0; i<16; i++) printf("%.2x", mm[i] & 0xff); printf("\n"); CSL_MD5("12345678901234567890123456789012345678901234567890123456789012345678901234567890", -1, mm); for (i=0; i<16; i++) printf("%.2x", mm[i] & 0xff); printf("\n"); return 0; } #endif /* * This is the end of the Eric Young code - what follows is Codemist * original code again. * * * * The next bit is for an experiment in controlling access to image files * etc. It is solely intended for use in implementing this access control * and is not made available as something that a CSL/Reduce user can access * directly. It favours high speed above other things, and much of its * security in use will be based on nobody having a real incentive to * poke at it since CSL-based images will not be expected to be of * sufficient value to justify the effort. */ int crypt_active; unsigned char *crypt_buffer; int crypt_count; /* * The following code was generated by running the program "gencry.c", * within which you can find the comments that explain what is going on. The * macro TIME_TEST could be defined to make this file more of a self- * contained test of its performance, but to do that you probably need * to look at the raw output from gencry.c. * * word length = 32 * shift register length = 65 * tap at position 18 * shuffle-buffer size = 4096 */ #ifdef TIME_TEST #include <stdio.h> #include <time.h> #define N 10000000 /* parameters for time test */ #define NSTARTS 4000 #define NTINY 50000000 #define KEY "Arthurs's sample key" typedef unsigned int uint32_t; #endif /* TIME_TEST */ static uint32_t lf[65], mix[4096]; #define R(x) ((x) >> 20) #define S(x) ((x) >> 18) #define T(x) ((x) << 13) /* * static unsigned char byte_order_test[] = * {1, 0, 0, 0, 0, 0, 0, 0}; */ #define CRYPT_BLOCK_SIZE 128 void crypt_get_block(unsigned char block[CRYPT_BLOCK_SIZE]) { uint32_t *b = (uint32_t *)block; int n; lf[0] -= lf[18]; lf[1] ^= lf[19]; lf[2] -= lf[20]; lf[3] += lf[21]; lf[4] += lf[22]; lf[5] -= lf[23]; lf[6] ^= lf[24]; lf[7] -= lf[25]; lf[8] += lf[26]; lf[9] ^= lf[27]; lf[10] -= lf[28]; lf[11] -= lf[29]; lf[12] += lf[30]; lf[13] += lf[31]; lf[14] -= lf[32]; lf[15] ^= lf[33]; lf[16] -= lf[34]; lf[17] += lf[35]; lf[18] += lf[36]; lf[19] += lf[37]; lf[20] -= lf[38]; lf[21] -= lf[39]; lf[22] ^= lf[40]; lf[23] += lf[41]; lf[24] -= lf[42]; lf[25] -= lf[43]; lf[26] += lf[44]; lf[27] += lf[45]; lf[28] -= lf[46]; lf[29] ^= lf[47]; lf[30] -= lf[48]; lf[31] += lf[49]; lf[32] -= lf[50]; lf[33] ^= lf[51]; lf[34] -= lf[52]; lf[35] ^= lf[53]; lf[36] += lf[54]; lf[37] += lf[55]; lf[38] ^= lf[56]; lf[39] ^= lf[57]; lf[40] += lf[58]; lf[41] -= lf[59]; lf[42] ^= lf[60]; lf[43] += lf[61]; lf[44] += lf[62]; lf[45] ^= lf[63]; lf[46] ^= lf[64]; lf[47] -= lf[0]; lf[48] ^= lf[1]; lf[49] ^= lf[2]; lf[50] ^= lf[3]; lf[51] ^= lf[4]; lf[52] ^= lf[5]; lf[53] ^= lf[6]; lf[54] += lf[7]; lf[55] -= lf[8]; lf[56] -= lf[9]; lf[57] ^= lf[10]; lf[58] -= lf[11]; lf[59] -= lf[12]; lf[60] ^= lf[13]; lf[61] += lf[14]; lf[62] ^= lf[15]; lf[63] -= lf[16]; lf[64] -= lf[17]; n = R(lf[0]); b[0] = mix[n]; mix[n] = (lf[54] + S(lf[29])) ^ T(lf[5]); n = R(lf[1]); b[1] = mix[n]; mix[n] = ~(lf[39] + S(lf[47])) + T(lf[15]); n = R(lf[2]); b[2] = mix[n]; mix[n] = (lf[25] + S(lf[14])) + T(lf[38]); n = R(lf[4]); b[3] = mix[n]; mix[n] = ~(lf[48] - S(lf[40])) ^ T(lf[10]); n = R(lf[5]); b[4] = mix[n]; mix[n] = (lf[44] - S(lf[55])) - T(lf[49]); n = R(lf[6]); b[5] = mix[n]; mix[n] = ~(lf[9] ^ S(lf[37])) + T(lf[50]); n = R(lf[8]); b[6] = mix[n]; mix[n] = (lf[64] ^ S(lf[51])) + T(lf[8]); n = R(lf[9]); b[7] = mix[n]; mix[n] = ~(lf[11] - S(lf[35])) - T(lf[21]); n = R(lf[10]); b[8] = mix[n]; mix[n] = (lf[20] ^ S(lf[21])) ^ T(lf[3]); n = R(lf[12]); b[9] = mix[n]; mix[n] = ~(lf[6] ^ S(lf[31])) - T(lf[61]); n = R(lf[13]); b[10] = mix[n]; mix[n] = (lf[3] - S(lf[16])) ^ T(lf[16]); n = R(lf[14]); b[11] = mix[n]; mix[n] = ~(lf[17] - S(lf[53])) - T(lf[2]); n = R(lf[16]); b[12] = mix[n]; mix[n] = (lf[27] + S(lf[42])) - T(lf[33]); n = R(lf[17]); b[13] = mix[n]; mix[n] = ~(lf[28] + S(lf[63])) - T(lf[46]); n = R(lf[18]); b[14] = mix[n]; mix[n] = (lf[10] - S(lf[46])) + T(lf[35]); n = R(lf[20]); b[15] = mix[n]; mix[n] = ~(lf[53] - S(lf[10])) - T(lf[27]); n = R(lf[21]); b[16] = mix[n]; mix[n] = (lf[4] + S(lf[18])) - T(lf[7]); n = R(lf[22]); b[17] = mix[n]; mix[n] = ~(lf[43] + S(lf[64])) ^ T(lf[45]); n = R(lf[24]); b[18] = mix[n]; mix[n] = (lf[14] + S(lf[26])) + T(lf[44]); n = R(lf[25]); b[19] = mix[n]; mix[n] = ~(lf[23] ^ S(lf[38])) + T(lf[58]); n = R(lf[26]); b[20] = mix[n]; mix[n] = (lf[47] + S(lf[59])) ^ T(lf[47]); n = R(lf[28]); b[21] = mix[n]; mix[n] = ~(lf[63] - S(lf[36])) - T(lf[57]); n = R(lf[29]); b[22] = mix[n]; mix[n] = (lf[56] + S(lf[4])) + T(lf[19]); n = R(lf[30]); b[23] = mix[n]; mix[n] = ~(lf[42] - S(lf[52])) - T(lf[56]); n = R(lf[32]); b[24] = mix[n]; mix[n] = (lf[37] + S(lf[3])) - T(lf[63]); n = R(lf[33]); b[25] = mix[n]; mix[n] = ~(lf[32] + S(lf[1])) - T(lf[12]); n = R(lf[34]); b[26] = mix[n]; mix[n] = (lf[62] - S(lf[39])) - T(lf[31]); n = R(lf[36]); b[27] = mix[n]; mix[n] = ~(lf[2] ^ S(lf[44])) ^ T(lf[18]); n = R(lf[37]); b[28] = mix[n]; mix[n] = (lf[24] ^ S(lf[50])) ^ T(lf[55]); n = R(lf[38]); b[29] = mix[n]; mix[n] = ~(lf[22] + S(lf[27])) - T(lf[32]); n = R(lf[40]); b[30] = mix[n]; mix[n] = (lf[51] + S(lf[33])) + T(lf[0]); n = R(lf[41]); b[31] = mix[n]; mix[n] = ~(lf[52] ^ S(lf[19])) - T(lf[26]); n = R(lf[42]); mix[n] = (lf[5] ^ S(lf[41])) + T(lf[28]); n = R(lf[44]); mix[n] = ~(lf[30] ^ S(lf[15])) - T(lf[30]); n = R(lf[45]); mix[n] = (lf[45] + S(lf[24])) ^ T(lf[51]); n = R(lf[46]); mix[n] = ~(lf[13] + S(lf[49])) - T(lf[11]); n = R(lf[48]); mix[n] = (lf[16] + S(lf[11])) - T(lf[39]); n = R(lf[49]); mix[n] = ~(lf[57] - S(lf[43])) - T(lf[60]); n = R(lf[50]); mix[n] = (lf[49] + S(lf[48])) ^ T(lf[25]); n = R(lf[52]); mix[n] = ~(lf[34] - S(lf[22])) ^ T(lf[23]); n = R(lf[53]); mix[n] = (lf[18] + S(lf[6])) + T(lf[1]); n = R(lf[54]); mix[n] = ~(lf[29] + S(lf[61])) - T(lf[64]); n = R(lf[56]); mix[n] = (lf[59] ^ S(lf[45])) - T(lf[41]); n = R(lf[57]); mix[n] = ~(lf[36] - S(lf[32])) + T(lf[37]); n = R(lf[58]); mix[n] = (lf[40] + S(lf[60])) + T(lf[14]); n = R(lf[60]); mix[n] = ~(lf[1] + S(lf[56])) ^ T(lf[36]); n = R(lf[61]); mix[n] = (lf[8] ^ S(lf[5])) ^ T(lf[17]); n = R(lf[62]); mix[n] = ~(lf[31] ^ S(lf[17])) ^ T(lf[52]); /* The test this way around favours Intel etc byte order */ if (((unsigned int *)byte_order_test)[0] != 1) { int i; for (i=0; i<32; i++) { uint32_t w = b[i]; uint32_t b0, b1, b2, b3; b0 = (w >> 24) & 0xffU; b1 = (w >> 8) & 0xff00U; b2 = (w << 8) & 0xff0000U; b3 = (w << 24) & 0xff000000U; b[i] = b0 | b1 | b2 | b3; } } return; } void crypt_init(char *key) { char *pk = key; unsigned char junk[CRYPT_BLOCK_SIZE]; int i, j; uint32_t w = 0; for (i=0; i<260; i++) { int k = *pk++; if (k == 0) pk = key; /* Cycle key (inc. terminating 0) */ w = (w << 8) | (k & 0xff); if ((i % 4) == 3) lf[i/4] = w; } for (i=0; i<4096; i++) mix[i] = 0; for (i=0; i<8; i++) { for (j=0; j<65; j++) lf[j] = (lf[j] << 10) | (lf[j] >> 22); lf[0] |= 1; for (j=0; j<64; j++) crypt_get_block(junk); } for (i=0; i<4096;) { int j; crypt_get_block(junk); for (j=0; j<32; j++) { uint32_t r = junk[4*j]; r = (r << 8) | junk[4*j+1]; r = (r << 8) | junk[4*j+2]; r = (r << 8) | junk[4*j+3]; if (r == 0) continue; mix[i++] ^= junk[j]; if (i == 4096) break; } } for (i=0; i<192; i++) crypt_get_block(junk); return; } #ifdef TIME_TEST /* * The main program here does not do anything of real interest. It * runs both the key-setup and the main loop lots of times and reports * how long it all takes. * * Here is some sample output from a Pentium-II 400Mhz system * * [02faf080] 7.60 nanoseconds to do tiny loop * 1.25 milliseconds to startup * rate = 104.86 megabytes per second * 79 a7 e1 52 2e 84 09 ce d0 3d 45 b2 52 2d b6 c7 * 9b ee 57 25 68 58 b7 44 42 51 1c c7 de 69 0f 89 * 98 6c cd 45 e0 a1 d4 04 a3 be 3d 5f 93 64 c9 d9 * b9 47 28 59 d0 99 5a 35 56 fd 89 e6 48 4f a4 88 * 7e dd 31 76 2b 8e 96 fa d0 6f d7 30 9c 3c 01 97 * 8a 54 93 c0 02 1d 26 df 31 2b 7b 92 56 51 fa 47 * 92 13 39 47 45 d2 b5 33 2b f6 cc 62 ec 73 00 40 * 66 ab 37 f5 1d 21 3a a9 b8 da 35 ac 04 f1 3b 53 * */ int main(int argc, char *argv[]) { clock_t c0, c1; unsigned char r[CRYPT_BLOCK_SIZE]; int i, j = 0; double rate; c0 = clock(); for (i=0; i<(NTINY+1); i++) j ^= i; c1 = clock(); printf("[%.8x] %.2f nanoseconds to do tiny loop\n", j, 1.0e9*(double)(c1-c0)/((double)CLOCKS_PER_SEC*(double)(NTINY+1))); c0 = clock(); for (i=0; i<NSTARTS; i++) crypt_init(KEY); c1 = clock(); printf("%.2f milliseconds to startup\n", 1000.0*(double)(c1-c0)/((double)CLOCKS_PER_SEC*(double)NSTARTS)); c0 = clock(); for (i=0; i<N; i++) crypt_get_block(r); c1 = clock(); rate = (double)N*(double)CRYPT_BLOCK_SIZE*(double)CLOCKS_PER_SEC/ ((double)(c1-c0)*1.0e6); printf("rate = %.2f megabytes per second\n", rate); for (i=0; i<128; i++) { printf("%.2x ", r[i]); if ((i % 16) == 15) printf("\n"); } return 0; } #endif /* TIME_TEST */ #undef R #undef S #undef T /* End of generated code... */ static void get_checksum(const setup_type *p) { while (p->name!=NULL) p++; if (p->one != NULL && p->two != NULL) { unsigned char *w = (unsigned char *)p->two; CSL_MD5_Update(w, strlen((char *)w)); } } void get_user_files_checksum(unsigned char *b) { CSL_MD5_Init(); get_checksum(u01_setup); get_checksum(u02_setup); get_checksum(u03_setup); get_checksum(u04_setup); get_checksum(u05_setup); get_checksum(u06_setup); get_checksum(u07_setup); get_checksum(u08_setup); get_checksum(u09_setup); get_checksum(u10_setup); get_checksum(u11_setup); get_checksum(u12_setup); CSL_MD5_Final(b); } char *crypt_keys[CRYPT_KEYS]; void setup(int restartp, double store_size) { int i; Lisp_Object nil; crypt_active = -1; if (restartp & 2) init_heap_segments(store_size); nil = C_nil; #ifdef TIDY_UP_MEMORY_AT_START /* * The following feature, which should not be neded, is liable to be * expensive on big machines because it touches all memory. * The code is left in case it helps with repeatability in the face * of accesses to uninitialised locations (ie BUGS). */ for (i=0; i<pages_count; i++) memset(pages[i], 0, (size_t)CSL_PAGE_SIZE+16); memset(stacksegment, 0, (size_t)stack_segsize*CSL_PAGE_SIZE+16); memset(nilsegment, 0, (size_t)NIL_SEGMENT_SIZE); #endif stack = stackbase; exit_tag = exit_value = nil; exit_reason = UNWIND_NULL; if (restartp & 1) { char junkbuf[120]; char filename[LONGEST_LEGAL_FILENAME]; if (IopenRoot(filename, 0, 0)) { term_printf("\n+++ Image file \"%s\" can not be read\n", filename); my_exit(EXIT_FAILURE); } /* * I read input via a buffer of size FREAD_BUFFER_SIZE, which I pre-fill * at this stage before I even try to read anything */ fread_ptr = (unsigned char *)stack; fread_count = Iread(fread_ptr, FREAD_BUFFER_SIZE); /* * I can adjust here (automatically) for whatever compression threshold * had been active when the image file was created. */ compression_worth_while = 128; crypt_active = -1; Cfread(junkbuf, 112); { int fg = junkbuf[111]; while (fg != 0) compression_worth_while <<= 1, fg--; /* * I do not really want to use encrypted images, and any such use suffers * from most of the usual problems of trying to protect information using * a scheme where an attacker has fairly direct access to all the information * needed to work around it! */ fg = junkbuf[110]; while (fg != 0) crypt_active++, fg--; if (crypt_active >= 0 && crypt_active < CRYPT_KEYS && crypt_keys[crypt_active] != NULL) { crypt_init(crypt_keys[crypt_active]); if ((crypt_buffer = (unsigned char *)(*malloc_hook)(CRYPT_BLOCK)) == NULL) crypt_active = -1; /* And will then fail */ crypt_count = 0; } } if (init_flags & INIT_VERBOSE) { term_printf("Created: %.25s\n", &junkbuf[64]); /* Time dump was taken */ } { unsigned char chk[16]; get_user_files_checksum(chk); for (i=0; i<16; i++) { if (chk[i] != (junkbuf[90+i] & 0xff)) { term_printf( "\n+++ Image file belongs with a different version\n"); term_printf( " of the executable file (incompatible code\n"); term_printf( " has been optimised into C and incorporated)\n"); term_printf( " Unable to use this image file, so stopping\n"); my_exit(EXIT_FAILURE); } } } /* * To make things more responsive for the user I will display a * banner rather early (before reading the bulk of the image file). * The banner that I will display is one provided to be by PRESERVE. */ { Ihandle save; char b[64]; int i; Icontext(&save); #define BANNER_CODE (-1002) if (IopenRoot(filename, BANNER_CODE, 0)) b[0] = 0; else { for (i=0; i<64; i++) b[i] = (char)Igetc(); IcloseInput(NO); } Irestore_context(save); /* * A banner set via startup-banner takes precedence over one from preserve. * But as a very special hack I detect if --texmacs was on the command * line and in that case I stay quiet... */ #ifdef HAVE_FWIN if (!texmacs_mode) #endif { if (b[0] != 0) { term_printf("%s\n", b); ensure_screen(); } else if (junkbuf[0] != 0) { term_printf("%s\n", junkbuf); ensure_screen(); } } } /* * From here on if crypt_active is >= 0 I will be decoding an encrypted * image file. */ Cfread(junkbuf, 8); Cfread((char *)BASE, sizeof(Lisp_Object)*last_nil_offset); copy_out_of_nilseg(YES); #ifndef COMMON qheader(nil) = TAG_ODDS+TYPE_SYMBOL+SYM_SPECIAL_VAR;/* BEFORE nil... */ #endif if ((byteflip & 0xffff0000U) == 0x56780000U) { flip_needed = NO; old_fp_rep = (int)(byteflip & FP_MASK); old_page_bits = (int)((byteflip >> 8) & 0x1f); } else if ((byteflip & 0x0000ffffU) == 0x00007856U) { flip_needed = YES; old_fp_rep = (int)(flip_32bits_fn(byteflip) & FP_MASK); old_page_bits = (int)((flip_32bits_fn(byteflip) >> 8) & 0x1f); } else { term_printf("\n+++ The checkpoint file is corrupt\n"); /* * Note: I use different numbers to check byte-ordering on segmented feature * non-segmented systems, since the heap image formats are not compatible. * A result will be that use of the wrong sort of image will lead to a * "checkpoint file corrupt" message rather than a more serious shambles. */ my_exit(EXIT_FAILURE); } if (old_page_bits == 0) old_page_bits = 16; /* Old default value */ /* * I could in fact recover in the case that old_page_bits < PAGE_BITS, since * I could just map the old small pages into the new big ones with a little * padding where needed. I will not do that JUST yet. In general it will * not be possible to load an image with large pages into a CSL that only * has small ones - eg there might be some vector that just would not fit * in the small page size. Even discounting that worry rearranging the * heap to allow for the discontinuities at the smaller page granularity would * be pretty painful. Again in the limit something very much akin to the * normal garbage collector could probably do it if it ever became really * necessary. */ if (old_page_bits != PAGE_BITS) { term_printf("\n+++ The checkpoint file was made on a machine\n"); term_printf("where CSL had been configured with a different page\n"); term_printf("size. It is not usable with this version.\n"); my_exit(EXIT_FAILURE); } /* The saved value of NIL is not needed in this case */ } else { for (i=first_nil_offset; i<last_nil_offset; i++) BASE[i] = nil; copy_out_of_nilseg(NO); } savestacklimit = stacklimit = &stack[stack_segsize*CSL_PAGE_SIZE/4-200]; /* allow some slop at end */ byteflip = 0x56780000 | ((int32_t)current_fp_rep & ~FP_WORD_ORDER) | (((int32_t)PAGE_BITS) << 8); native_pages_changed = 0; if ((restartp & 1) != 0) warm_setup((restartp & 4) != 0); else cold_setup((restartp & 4) != 0); if (init_flags & INIT_QUIET) Lverbos(nil, fixnum_of_int(1)); if (init_flags & INIT_VERBOSE) Lverbos(nil, fixnum_of_int(3)); /* * Here I grab more memory (if I am allowed to) until the proportion of the * heap active at the end of garbage collection is less than 1/2. If the * attempt to grab more memory fails I clear the bit in init_flags that * allows me to try to expand, so I will not waste time again. * The aim of keeping the heap less than half full is an heuristic and * could be adjusted on the basis of experience with this code. */ if (init_flags & INIT_EXPANDABLE) { int32_t more = heap_pages_count + vheap_pages_count + bps_pages_count + native_pages_count; more = 3 *more - pages_count; while (more-- > 0) { void *page = (void *)my_malloc_1((size_t)(CSL_PAGE_SIZE + 16)); /* * CF the code in gc.c -- I can still use my_malloc_1 here, which makes this * code just a tiny bit safer. */ intptr_t pun = (intptr_t)page; intptr_t pun1 = (intptr_t)((char *)page + CSL_PAGE_SIZE + 16); if ((pun ^ pun1) < 0) page = NULL; if ((pun + address_sign) < 0) page = NULL; if (page == NULL) { init_flags &= ~INIT_EXPANDABLE; break; } else pages[pages_count++] = page; } } { int32_t w = 0; /* * The total store allocated is that used plus that free, including the * page set aside for the Lisp stack. */ if (init_flags & INIT_VERBOSE) term_printf("Memory allocation: %ld bytes\n", (long)CSL_PAGE_SIZE*(pages_count+w+1)); } #ifdef MEMORY_TRACE #ifndef CHECK_ONLY memory_comment(15); #endif #endif return; } void copy_into_nilseg(int fg) { Lisp_Object nil = C_nil; #ifdef NILSEG_EXTERNS int i; if (fg) /* move non list bases too */ { *(uint32_t *)&BASE[12] = byteflip; BASE[13] = codefringe; *(Lisp_Object volatile *)&BASE[14] = codelimit; /* * The messing around here is to ensure that on 64-bit architectures * stacklimit is kept properly aligned. */ #ifdef COMMON *(Lisp_Object * volatile *)&BASE[16] = stacklimit; #else *(Lisp_Object * volatile *)&BASE[15] = stacklimit; #endif BASE[18] = fringe; *(Lisp_Object volatile *)&BASE[19] = heaplimit; *(Lisp_Object volatile *)&BASE[20] = vheaplimit; BASE[21] = vfringe; *(uint32_t *)&BASE[22] = miscflags; *(int32_t *)&BASE[24] = nwork; *(int32_t *)&BASE[25] = exit_reason; *(int32_t *)&BASE[26] = exit_count; *(uint32_t *)&BASE[27] = gensym_ser; *(uint32_t *)&BASE[28] = print_precision; *(int32_t *)&BASE[29] = current_modulus; *(int32_t *)&BASE[30] = fastget_size; *(int32_t *)&BASE[31] = package_bits; } /* * Entries 50 and 51 are used for chains of hash tables, and so get * very special individual treatment. */ BASE[52] = current_package; BASE[53] = B_reg; BASE[54] = codevec; BASE[55] = litvec; BASE[56] = exit_tag; BASE[57] = exit_value; BASE[58] = catch_tags; BASE[59] = lisp_package; BASE[60] = boffo; BASE[61] = charvec; BASE[62] = sys_hash_table; BASE[63] = help_index; BASE[64] = gensym_base; BASE[65] = err_table; BASE[66] = supervisor; BASE[67] = startfn; BASE[68] = faslvec; BASE[69] = tracedfn; BASE[70] = prompt_thing; BASE[71] = faslgensyms; BASE[72] = cl_symbols; BASE[73] = active_stream; BASE[74] = current_module; BASE[75] = native_defs; BASE[90] = append_symbol; BASE[91] = applyhook; BASE[92] = cfunarg; BASE[93] = comma_at_symbol; BASE[94] = comma_symbol; BASE[95] = compiler_symbol; BASE[96] = comp_symbol; BASE[97] = cons_symbol; BASE[98] = echo_symbol; BASE[99] = emsg_star; BASE[100] = evalhook; BASE[101] = eval_symbol; BASE[102] = expr_symbol; BASE[103] = features_symbol; BASE[104] = fexpr_symbol; BASE[105] = funarg; BASE[106] = function_symbol; BASE[107] = lambda; BASE[108] = lisp_true; BASE[109] = lower_symbol; BASE[110] = macroexpand_hook; BASE[111] = macro_symbol; BASE[112] = opt_key; BASE[113] = prinl_symbol; BASE[114] = progn_symbol; BASE[115] = quote_symbol; BASE[116] = raise_symbol; BASE[117] = redef_msg; BASE[118] = rest_key; BASE[119] = savedef; BASE[120] = string_char_sym; BASE[121] = unset_var; BASE[122] = work_symbol; BASE[123] = lex_words; BASE[124] = get_counts; BASE[125] = fastget_names; BASE[126] = input_libraries; BASE[127] = output_library; BASE[128] = current_file; BASE[129] = break_function; BASE[130] = lisp_work_stream; BASE[131] = lisp_standard_output; BASE[132] = lisp_standard_input; BASE[133] = lisp_debug_io; BASE[134] = lisp_error_output; BASE[135] = lisp_query_io; BASE[136] = lisp_terminal_io; BASE[137] = lisp_trace_output; BASE[138] = standard_output; BASE[139] = standard_input; BASE[140] = debug_io; BASE[141] = error_output; BASE[142] = query_io; BASE[143] = terminal_io; BASE[144] = trace_output; BASE[145] = fasl_stream; BASE[146] = native_code; BASE[147] = native_symbol; BASE[148] = traceprint_symbol; BASE[149] = loadsource_symbol; BASE[150] = hankaku_symbol; BASE[151] = bytecoded_symbol; BASE[152] = nativecoded_symbol; BASE[153] = gchook; #ifdef COMMON BASE[170] = keyword_package; BASE[171] = all_packages; BASE[172] = package_symbol; BASE[173] = internal_symbol; BASE[174] = external_symbol; BASE[175] = inherited_symbol; BASE[176] = key_key; BASE[177] = allow_other_keys; BASE[178] = aux_key; BASE[179] = format_symbol; BASE[180] = expand_def_symbol; BASE[181] = allow_key_key; BASE[182] = declare_symbol; BASE[183] = special_symbol; #endif for (i=0; i<=50; i++) BASE[work_0_offset+i] = workbase[i]; #endif /* NILSEG_EXTERNS */ if (fg) { #ifdef COMMON *(Lisp_Object * volatile *)&BASE[16] = stacklimit; #else *(Lisp_Object * volatile *)&BASE[15] = stacklimit; #endif } BASE[190] = user_base_0; BASE[191] = user_base_1; BASE[192] = user_base_2; BASE[193] = user_base_3; BASE[194] = user_base_4; BASE[195] = user_base_5; BASE[196] = user_base_6; BASE[197] = user_base_7; BASE[198] = user_base_8; BASE[199] = user_base_9; } void copy_out_of_nilseg(int fg) { Lisp_Object nil = C_nil; #ifdef NILSEG_EXTERNS int i; if (fg) { byteflip = *(uint32_t *)&BASE[12]; codefringe = BASE[13]; codelimit = *(Lisp_Object volatile *)&BASE[14]; fringe = BASE[18]; heaplimit = *(Lisp_Object volatile *)&BASE[19]; vheaplimit = *(Lisp_Object volatile *)&BASE[20]; vfringe = BASE[21]; miscflags = *(uint32_t *)&BASE[22]; nwork = *(int32_t *)&BASE[24]; exit_reason = *(int32_t *)&BASE[25]; exit_count = *(int32_t *)&BASE[26]; gensym_ser = *(uint32_t *)&BASE[27]; print_precision = *(uint32_t *)&BASE[28]; current_modulus = *(int32_t *)&BASE[29]; fastget_size = *(int32_t *)&BASE[30]; package_bits = *(int32_t *)&BASE[31]; } current_package = BASE[52]; B_reg = BASE[53]; codevec = BASE[54]; litvec = BASE[55]; exit_tag = BASE[56]; exit_value = BASE[57]; catch_tags = BASE[58]; lisp_package = BASE[59]; boffo = BASE[60]; charvec = BASE[61]; sys_hash_table = BASE[62]; help_index = BASE[63]; gensym_base = BASE[64]; err_table = BASE[65]; supervisor = BASE[66]; startfn = BASE[67]; faslvec = BASE[68]; tracedfn = BASE[69]; prompt_thing = BASE[70]; faslgensyms = BASE[71]; cl_symbols = BASE[72]; active_stream = BASE[73]; current_module = BASE[74]; native_defs = BASE[75]; append_symbol = BASE[90]; applyhook = BASE[91]; cfunarg = BASE[92]; comma_at_symbol = BASE[93]; comma_symbol = BASE[94]; compiler_symbol = BASE[95]; comp_symbol = BASE[96]; cons_symbol = BASE[97]; echo_symbol = BASE[98]; emsg_star = BASE[99]; evalhook = BASE[100]; eval_symbol = BASE[101]; expr_symbol = BASE[102]; features_symbol = BASE[103]; fexpr_symbol = BASE[104]; funarg = BASE[105]; function_symbol = BASE[106]; lambda = BASE[107]; lisp_true = BASE[108]; lower_symbol = BASE[109]; macroexpand_hook = BASE[110]; macro_symbol = BASE[111]; opt_key = BASE[112]; prinl_symbol = BASE[113]; progn_symbol = BASE[114]; quote_symbol = BASE[115]; raise_symbol = BASE[116]; redef_msg = BASE[117]; rest_key = BASE[118]; savedef = BASE[119]; string_char_sym = BASE[120]; unset_var = BASE[121]; work_symbol = BASE[122]; lex_words = BASE[123]; get_counts = BASE[124]; fastget_names = BASE[125]; input_libraries = BASE[126]; output_library = BASE[127]; current_file = BASE[128]; break_function = BASE[129]; lisp_work_stream = BASE[130]; lisp_standard_output = BASE[131]; lisp_standard_input = BASE[132]; lisp_debug_io = BASE[133]; lisp_error_output = BASE[134]; lisp_query_io = BASE[135]; lisp_terminal_io = BASE[136]; lisp_trace_output = BASE[137]; standard_output = BASE[138]; standard_input = BASE[139]; debug_io = BASE[140]; error_output = BASE[141]; query_io = BASE[142]; terminal_io = BASE[143]; trace_output = BASE[144]; fasl_stream = BASE[145]; native_code = BASE[146]; native_symbol = BASE[147]; traceprint_symbol = BASE[148]; loadsource_symbol = BASE[149]; hankaku_symbol = BASE[150]; bytecoded_symbol = BASE[151]; nativecoded_symbol = BASE[152]; gchook = BASE[153]; #ifdef COMMON keyword_package = BASE[170]; all_packages = BASE[171]; package_symbol = BASE[172]; internal_symbol = BASE[173]; external_symbol = BASE[174]; inherited_symbol = BASE[175]; key_key = BASE[176]; allow_other_keys = BASE[177]; aux_key = BASE[178]; format_symbol = BASE[179]; expand_def_symbol = BASE[180]; allow_key_key = BASE[181]; declare_symbol = BASE[182]; special_symbol = BASE[183]; #endif for (i = 0; i<=50; i++) workbase[i] = BASE[work_0_offset+i]; #endif /* NILSEG_EXTERNS */ if (fg) { #ifdef COMMON stacklimit = *(Lisp_Object *volatile *)&BASE[16]; #else stacklimit = *(Lisp_Object *volatile *)&BASE[15]; #endif } user_base_0 = BASE[190]; user_base_1 = BASE[191]; user_base_2 = BASE[192]; user_base_3 = BASE[193]; user_base_4 = BASE[194]; user_base_5 = BASE[195]; user_base_6 = BASE[196]; user_base_7 = BASE[197]; user_base_8 = BASE[198]; user_base_9 = BASE[199]; } /* end of restart.c */