comparison keiko/loader.c @ 0:bfdcc3820b32

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