annotate keiko/loader.c @ 1:b5139af1a420 tip basis

Fixed permissions on compile scripts
author Mike Spivey <mike@cs.ox.ac.uk>
date Fri, 13 Oct 2017 17:27:58 +0100
parents bfdcc3820b32
children
rev   line source
mike@0 1 /*
mike@0 2 * loader.c
mike@0 3 *
mike@0 4 * This file is part of the Oxford Oberon-2 compiler
mike@0 5 * Copyright (c) 2006--2016 J. M. Spivey
mike@0 6 * All rights reserved
mike@0 7 *
mike@0 8 * Redistribution and use in source and binary forms, with or without
mike@0 9 * modification, are permitted provided that the following conditions are met:
mike@0 10 *
mike@0 11 * 1. Redistributions of source code must retain the above copyright notice,
mike@0 12 * this list of conditions and the following disclaimer.
mike@0 13 * 2. Redistributions in binary form must reproduce the above copyright notice,
mike@0 14 * this list of conditions and the following disclaimer in the documentation
mike@0 15 * and/or other materials provided with the distribution.
mike@0 16 * 3. The name of the author may not be used to endorse or promote products
mike@0 17 * derived from this software without specific prior written permission.
mike@0 18 *
mike@0 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
mike@0 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
mike@0 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
mike@0 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
mike@0 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
mike@0 24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
mike@0 25 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
mike@0 26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
mike@0 27 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
mike@0 28 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
mike@0 29 */
mike@0 30
mike@0 31 #include "obx.h"
mike@0 32 #include "keiko.h"
mike@0 33 #include "exec.h"
mike@0 34
mike@0 35 static FILE *binfp;
mike@0 36
mike@0 37 static int binread(void *buf, int size) {
mike@0 38 return fread(buf, 1, size, binfp);
mike@0 39 }
mike@0 40
mike@0 41 static int bingetc(void) {
mike@0 42 char buf[1];
mike@0 43 if (binread(buf, 1) == 0) return EOF;
mike@0 44 return buf[0];
mike@0 45 }
mike@0 46
mike@0 47 /* read_string -- input a null-terminated string, allocate space dynamically */
mike@0 48 static char *read_string() {
mike@0 49 int n = 0;
mike@0 50 int c;
mike@0 51 char *p;
mike@0 52 char buf[256];
mike@0 53
mike@0 54 do {
mike@0 55 c = bingetc();
mike@0 56 if (c == EOF) panic("*unexpected EOF");
mike@0 57 buf[n++] = c;
mike@0 58 } while (c != '\0');
mike@0 59
mike@0 60 p = (char *) scratch_alloc(n);
mike@0 61 strcpy(p, buf);
mike@0 62 return p;
mike@0 63 }
mike@0 64
mike@0 65 /* get_int -- get a 4-byte value in portable byte order */
mike@0 66 static int get_int(uchar *p) {
mike@0 67 return (p[3]<<24) + (p[2]<<16) + (p[1]<<8) + p[0];
mike@0 68 }
mike@0 69
mike@0 70
mike@0 71 /* read_int -- input a 4-byte value in portable byte order */
mike@0 72 static int read_int() {
mike@0 73 uchar buf[4];
mike@0 74 binread(buf, 4);
mike@0 75 return get_int(buf);
mike@0 76 }
mike@0 77
mike@0 78 /* Here is the still centre of the whirling vortex that is byte-order
mike@0 79 independence. The compiler output, Kieko assembly language, is
mike@0 80 plain text. The assembler/linker translates this into a byte-order
mike@0 81 independent file of object code.
mike@0 82
mike@0 83 The bytecode in this file contains one and two byte embedded
mike@0 84 constants that are in little-endian order, and the bytecode
mike@0 85 interpreter puts the bytes together where necessary, respecting the
mike@0 86 little-endian order in the code even on a big-endian machine. (It
mike@0 87 has to address bytecode one byte a time anyway, because of
mike@0 88 alignment restrictions.)
mike@0 89
mike@0 90 The data segment in the object code consists of 4-byte words, and
mike@0 91 these are relocated when the program is loaded. Some of these
mike@0 92 words contain character data for string constants, and they require
mike@0 93 no relocation. Some words contain integer or floating-point
mike@0 94 constants, and they are relocated by swapping the byte order if
mike@0 95 necessary. Finally, some words contain addresses in the data or
mike@0 96 code segment, and they are relocated by swapping the byte order as
mike@0 97 needed, and adding the base address of the segment in question.
mike@0 98 Thus in the running program, both the memory and the evaluation
mike@0 99 stack contain only values in native byte order -- and all pointers
mike@0 100 are represented as absolute addresses, enabling the program to live
mike@0 101 in harmony with a conservative garbage collector.
mike@0 102
mike@0 103 One final twist: double-precision values are always stored as two
mike@0 104 words, with each word in native byte order, but with the less
mike@0 105 significant word first, even on a big-endian machine. This is ok,
mike@0 106 because these values are always loaded and stored one word at a
mike@0 107 time, and assembled into native order immediately before doing
mike@0 108 arithmetic. */
mike@0 109
mike@0 110 #define REL_BLOCK 1024
mike@0 111
mike@0 112 /* relocate -- read relocation data */
mike@0 113 static void relocate(int size) {
mike@0 114 unsigned reloc[REL_BLOCK];
mike@0 115 int n;
mike@0 116
mike@0 117 for (int base = 0; base < size; base += n) {
mike@0 118 n = min(size - base, REL_BLOCK * CODES_PER_WORD * WORD_SIZE);
mike@0 119 int nwords = (n/WORD_SIZE+CODES_PER_WORD-1)/CODES_PER_WORD;
mike@0 120 binread(reloc, nwords * sizeof(unsigned));
mike@0 121
mike@0 122 for (int i = 0; i < n; i += WORD_SIZE) {
mike@0 123 int rbits = reloc_bits(reloc, i/WORD_SIZE);
mike@0 124
mike@0 125 #ifdef DEBUG
mike@0 126 if (dflag >= 2)
mike@0 127 printf("Reloc %d %d\n", base+i, rbits);
mike@0 128 #endif
mike@0 129
mike@0 130 int m = get_int(&dmem[base+i]);
mike@0 131 value *p = (value *) &dmem[base+i];
mike@0 132
mike@0 133 switch (rbits) {
mike@0 134 case R_WORD:
mike@0 135 (*p).i = m;
mike@0 136 break;
mike@0 137 case R_DATA:
mike@0 138 (*p).a = address(dmem + m);
mike@0 139 break;
mike@0 140 case R_CODE:
mike@0 141 (*p).a = address(imem + m);
mike@0 142 break;
mike@0 143 case R_SUBR:
mike@0 144 switch (m) {
mike@0 145 case INTERP: (*p).a = interpreter; break;
mike@0 146 case DLTRAP: (*p).a = dyntrap; break;
mike@0 147 default:
mike@0 148 panic("bad subr code");
mike@0 149 }
mike@0 150 break;
mike@0 151 }
mike@0 152 }
mike@0 153 }
mike@0 154 }
mike@0 155
mike@0 156 /* read_symbols -- read symbol table */
mike@0 157 static void read_symbols(int dseg) {
mike@0 158 uchar *addr;
mike@0 159 int chksum, nlines;
mike@0 160 int nm = 0, np = 0;
mike@0 161 #ifdef DEBUG
mike@0 162 const char *kname;
mike@0 163 #define debug_kind(n) kname = n
mike@0 164 #else
mike@0 165 #define debug_kind(n)
mike@0 166 #endif
mike@0 167
mike@0 168 modtab = (module *) scratch_alloc(nmods * sizeof(module));
mike@0 169 proctab = (proc *) scratch_alloc(nprocs * sizeof(proc));
mike@0 170
mike@0 171 for (int i = 0; i < nsyms; i++) {
mike@0 172 int kind = read_int();
mike@0 173 char *name = read_string();
mike@0 174
mike@0 175 switch (kind) {
mike@0 176 case X_MODULE:
mike@0 177 debug_kind("Module");
mike@0 178 addr = dmem + read_int();
mike@0 179 chksum = read_int();
mike@0 180 nlines = read_int();
mike@0 181 modtab[nm++] = make_module(name, addr, chksum, nlines);
mike@0 182 break;
mike@0 183
mike@0 184 case X_PROC:
mike@0 185 debug_kind("Proc");
mike@0 186 addr = dmem + read_int();
mike@0 187 proctab[np++] = make_proc(name, addr);
mike@0 188 break;
mike@0 189
mike@0 190 case X_DATA:
mike@0 191 debug_kind("Data");
mike@0 192 addr = dmem + read_int();
mike@0 193 make_symbol("data", name, addr);
mike@0 194 break;
mike@0 195
mike@0 196 case X_LINE:
mike@0 197 debug_kind("Line");
mike@0 198 addr = imem + read_int();
mike@0 199 make_symbol("line", name, addr);
mike@0 200 break;
mike@0 201
mike@0 202 default:
mike@0 203 debug_kind("Unknown");
mike@0 204 addr = NULL;
mike@0 205 panic("*bad symbol %s", name);
mike@0 206 }
mike@0 207
mike@0 208 #ifdef DEBUG
mike@0 209 if (dflag >= 1) printf("%s %s = %p\n", kname, name, addr);
mike@0 210 #endif
mike@0 211 }
mike@0 212
mike@0 213 if (nm != nmods || np != nprocs)
mike@0 214 panic("*symbol counts don't match (mods %d/%d, procs %d/%d)\n",
mike@0 215 nm, nmods, np, nprocs);
mike@0 216
mike@0 217 /* Calculate module lengths */
mike@0 218 addr = dmem + dseg;
mike@0 219 for (int i = nmods-1; i >= 0; i--) {
mike@0 220 modtab[i]->m_length = addr - modtab[i]->m_addr;
mike@0 221 addr = modtab[i]->m_addr;
mike@0 222 }
mike@0 223 }
mike@0 224
mike@0 225 /* load_file -- load a file of object code */
mike@0 226 void load_file(FILE *bfp) {
mike@0 227 /* Get trailer */
mike@0 228 trailer t;
mike@0 229 fseek(bfp, - (long) sizeof(trailer), SEEK_END);
mike@0 230 int nread = fread(&t, 1, sizeof(trailer), bfp);
mike@0 231 if (nread != sizeof(trailer)) panic("couldn't read trailer");
mike@0 232
mike@0 233 /* Check magic numbers */
mike@0 234 if (nread < sizeof(trailer))
mike@0 235 panic("couldn't read trailer");
mike@0 236 if (strncmp((char *) t.magic, MAGIC, 4) != 0)
mike@0 237 panic("bad magic number\n%s",
mike@0 238 "[The program you are running is not a valid"
mike@0 239 " Oberon bytecode file]");
mike@0 240 if (get_int(t.sig) != SIG)
mike@0 241 panic("bad signature %#0.8x\n%s\n%s", get_int(t.sig),
mike@0 242 "[Although this appears to be an Oberon bytecode file,",
mike@0 243 " it needs a different version of the runtime system]");
mike@0 244
mike@0 245 /* Decode the other data */
mike@0 246 int seglen[NSEGS];
mike@0 247 for (int i = 0; i < NSEGS; i++)
mike@0 248 seglen[i] = get_int(t.segment[i]);
mike@0 249
mike@0 250 code_size = seglen[S_CODE];
mike@0 251 stack_size = seglen[S_STACK];
mike@0 252
mike@0 253 nmods = get_int(t.nmods); nprocs = get_int(t.nprocs);
mike@0 254 nsyms = get_int(t.nsyms);
mike@0 255 int start = get_int(t.start);
mike@0 256
mike@0 257 #ifdef DEBUG
mike@0 258 if (dflag >= 1) {
mike@0 259 printf("csize = %d, dsize = %d, bss = %d, stk = %d\n",
mike@0 260 seglen[S_CODE], seglen[S_DATA],
mike@0 261 seglen[S_BSS], seglen[S_STACK]);
mike@0 262 printf("nmods = %d, nprocs = %d, nsyms = %d\n",
mike@0 263 nmods, nprocs, nsyms);
mike@0 264 }
mike@0 265 #endif
mike@0 266
mike@0 267 fseek(bfp, start, SEEK_END);
mike@0 268 binfp = bfp;
mike@0 269
mike@0 270 /* Load the code */
mike@0 271 imem = (uchar *) scratch_alloc(seglen[S_CODE]);
mike@0 272 binread(imem, seglen[S_CODE]);
mike@0 273
mike@0 274 /* Load and relocate the data */
mike@0 275 dmem = (uchar *) scratch_alloc(seglen[S_DATA]+seglen[S_BSS]);
mike@0 276 binread(dmem, seglen[S_DATA]);
mike@0 277 relocate(seglen[S_DATA]);
mike@0 278 memset(dmem+seglen[S_DATA], 0, seglen[S_BSS]);
mike@0 279
mike@0 280 /* Allocate stack */
mike@0 281 stack = (uchar *) scratch_alloc(stack_size);
mike@0 282
mike@0 283 /* Save the entry point, pointer map and library path */
mike@0 284 entry = (value *) &dmem[get_int(t.entry)];
mike@0 285 gcmap = (value *) &dmem[get_int(t.gcmap)];
mike@0 286 if (get_int(t.libdir) != 0)
mike@0 287 libpath = (char *) &dmem[get_int(t.libdir)];
mike@0 288
mike@0 289 /* Read the symbols */
mike@0 290 if (nsyms > 0) read_symbols(seglen[S_DATA]);
mike@0 291 }