comparison keiko/oblink.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 * oblink.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 #define EXTERN
32 #include "oblink.h"
33 #include "keiko.h"
34
35 const char *version =
36 "Oxford Oberon-2 linker version " PACKAGE_VERSION " [build " REVID "]";
37 const char *copyright = "Copyright (C) 1999--2012 J. M. Spivey";
38
39 /* The module table has one entry for each module that appears in the
40 input files. There's another table kept by the linker itself that
41 has one entry for each module actually selected for linking. */
42
43 struct _module {
44 char *m_file; /* Name of the file */
45 char *m_name; /* Name of the module */
46 mybool m_lib, m_needed; /* Whether a library module, whether needed */
47 int m_dep; /* Index of first prerequisite */
48 int m_check; /* Checksum */
49 };
50
51 static growdecl(module);
52 #define module growbuf(module, struct _module)
53 #define nmodules growsize(module)
54
55 /* The imports of module m are dep[module[m].m_dep .. module[m+1].m_dep) */
56
57 static growdecl(dep);
58 #define dep growbuf(dep, int)
59 #define ndeps growsize(dep)
60
61 #ifdef HAVE_GETOPT_LONG_ONLY
62 static int nfiles;
63 static char **file;
64 #else
65 static growdecl(file);
66 #define file growbuf(file, char *)
67 #define nfiles growsize(file)
68 #endif
69
70 #define MAXLINE 1024
71
72 static char line[MAXLINE];
73 static int nwords;
74 static char *words[MAXWORDS];
75
76 static mybool stdlib = TRUE;
77 static char *lscript = (char *) "lscript";
78 static char *interp = NULL;
79 static char *outname = (char *) "a.out";
80 static char *libdir = NULL;
81 static char *rtlibdir = NULL;
82
83 static int find_module(char *name) {
84 for (int i = 0; i < nmodules; i++)
85 if (strcmp(name, module[i].m_name) == 0)
86 return i;
87
88 return -1;
89 }
90
91 /* scan -- scan a file for MODULE and IMPORT directives */
92 static void scan(char *name, mybool islib) {
93 FILE *fp;
94 int m = -1, m2, chksum;
95
96 err_file = must_strdup(name);
97 fp = fopen(name, "r");
98 if (fp == NULL) {
99 perror(name);
100 exit(2);
101 }
102
103 while (fgets(line, MAXLINE, fp) != NULL) {
104 nwords = split_line(line, words);
105 if (nwords == 0) continue;
106
107 if (strcmp(words[0], "MODULE") == 0) {
108 char *mname = words[1];
109 m = find_module(mname);
110 if (m >= 0) {
111 if (module[m].m_lib)
112 error("%s has the same name as a library module",
113 words[1]);
114 else
115 error("%s is loaded more than once", words[1]);
116 }
117
118 buf_grow(module);
119 m = nmodules;
120 module[m].m_file = name;
121 module[m].m_name = must_strdup(mname);
122 module[m].m_lib = islib;
123 module[m].m_needed = FALSE;
124 module[m].m_dep = ndeps;
125 module[m].m_check = strtoul(words[2], NULL, 0);
126 nmodules++;
127 } else if (strcmp(words[0], "IMPORT") == 0) {
128 if (m < 0)
129 error("IMPORT appears before MODULE in %s", name);
130
131 m2 = find_module(words[1]);
132 chksum = strtoul(words[2], NULL, 0);
133 buf_grow(dep);
134 if (m2 < 0)
135 error("%s imports %s -- please load it first",
136 module[m].m_name, words[1]);
137 else {
138 dep[ndeps++] = m2;
139 if (module[m2].m_check != chksum)
140 error("checksum of module %s does not match value"
141 " expected by module %s",
142 words[1], module[m].m_name);
143 }
144 } else if (strcmp(words[0], "ENDHDR") == 0) {
145 break;
146 } else {
147 panic("*bad directive %s in file header", words[0]);
148 }
149 }
150
151 fclose(fp);
152 }
153
154 static void scan_files(void) {
155 if (stdlib) {
156 char buf[128];
157 sprintf(buf, "%s%s%s", libdir, DIRSEP, lscript);
158 FILE *fp = fopen(buf, "r");
159 if (fp == NULL) {
160 perror(buf);
161 exit(2);
162 }
163
164 while (fgets(line, MAXLINE, fp) != NULL) {
165 line[strlen(line)-1] = '\0';
166 sprintf(buf, "%s%s%s", libdir, DIRSEP, line);
167 scan(must_strdup(buf), TRUE);
168 }
169
170 fclose(fp);
171 }
172
173 for (int i = 0; i < nfiles; i++)
174 scan(file[i], FALSE);
175 }
176
177 /* load_needed -- load files containing needed modules */
178 static void load_needed() {
179 for (int i = 0; i < nmodules; i++) {
180 if (!module[i].m_needed) continue;
181
182 char *name = module[i].m_file;
183 err_file = name;
184 FILE *fp = fopen(name, "r");
185 if (fp == NULL) {
186 perror(name);
187 exit(2);
188 }
189
190 while (fgets(line, MAXLINE, fp) != NULL) {
191 nwords = split_line(line, words);
192 if (nwords == 0) continue;
193 put_inst(words[0], &words[1], nwords-1);
194 }
195
196 fclose(fp);
197 }
198 }
199
200 /* trace_imports -- compute needed modules */
201 static void trace_imports(void) {
202 for (int i = nmodules-1; i >= 0; i--) {
203 if (!module[i].m_lib || strcmp(module[i].m_name, "_Builtin") == 0)
204 module[i].m_needed = TRUE;
205
206 if (module[i].m_needed)
207 for (int j = module[i].m_dep; j < module[i+1].m_dep; j++)
208 module[dep[j]].m_needed = TRUE;
209 }
210
211 #ifdef DEBUG
212 if (dflag) {
213 fprintf(stderr, "Needed:");
214 for (int i = 0; i < nmodules; i++)
215 if (module[i].m_needed)
216 fprintf(stderr, " %s", module[i].m_name);
217 fprintf(stderr, "\n");
218 }
219 #endif
220 }
221
222 /* gen_main -- generate the main program */
223 static void gen_main(void) {
224 char buf[128];
225
226 if (known("MAIN")) return;
227
228 err_file = (char *) "main program";
229
230 /* For completeness, generate a header listing all loaded modules. */
231 gen_inst("MODULE %%Main 0 0");
232 for (int i = 0; i < nmodules; i++) {
233 if (strcmp(module[i].m_name, "_Builtin") == 0
234 || !module[i].m_needed) continue;
235 gen_inst("IMPORT %s %#x", module[i].m_name, module[i].m_check);
236 }
237 gen_inst("ENDHDR");
238
239 gen_inst("PROC MAIN 0 4 0");
240 /* Code to call each module body */
241 for (int i = 0; i < nmodules; i++) {
242 if (!module[i].m_needed) continue;
243 sprintf(buf, "%s.%%main", module[i].m_name);
244 if (known(buf)) {
245 gen_inst("GLOBAL %s", buf);
246 gen_inst("CALL 0");
247 }
248 }
249 gen_inst("RETURN");
250 gen_inst("END");
251
252 /* Make global pointer map */
253 gen_inst("DEFINE GCMAP");
254 for (int i = 0; i < nmodules; i++) {
255 if (!module[i].m_needed) continue;
256 sprintf(buf, "%s.%%gcmap", module[i].m_name);
257 if (known(buf)) {
258 gen_inst("WORD GC_MAP");
259 gen_inst("WORD %s", buf);
260 }
261 }
262 gen_inst("WORD GC_END");
263 }
264
265 #include <getopt.h>
266
267 #define SCRIPT 1
268
269 static struct option longopts[] = {
270 { "script", required_argument, NULL, SCRIPT },
271 { "nostdlib", no_argument, &stdlib, FALSE },
272 { "pl", no_argument, &linecount, TRUE },
273 { NULL, 0, NULL, 0 }
274 };
275
276 /* get_options -- analyse arguments */
277 static void get_options(int argc, char **argv) {
278 for (;;) {
279 int c = getopt_long_only(argc, argv, "dvsgCi:L:R:o:k:",
280 longopts, NULL);
281
282 if (c == -1) break;
283
284 switch (c) {
285 case 'd':
286 dflag++; break;
287 case 'v':
288 printf("%s\n", version);
289 exit(0);
290 break;
291 case 's':
292 sflag = TRUE; break;
293 case 'g':
294 gflag = TRUE; break;
295 case 'C':
296 custom = TRUE; break;
297 case 'i':
298 interp = optarg; break;
299 case 'L':
300 libdir = optarg; break;
301 case 'R':
302 rtlibdir = optarg; break;
303 case 'o':
304 outname = optarg; break;
305 case 'k':
306 stack_size = atoi(optarg);
307 if (stack_size < MIN_STACK) stack_size = MIN_STACK;
308 break;
309 case 0:
310 /* Long option with flag */
311 break;
312 case SCRIPT:
313 /* -script */
314 lscript = optarg; break;
315 case '?':
316 /* Error has been reported by getopt */
317 exit(2);
318 break;
319 default:
320 panic("*bad option");
321 }
322 }
323
324 nfiles = argc - optind;
325 file = &argv[optind];
326 }
327
328 int main(int argc, char **argv) {
329 progname = argv[0];
330
331 buf_init(module, INIT_MODS, 1, struct _module, "modules");
332 buf_init(dep, INIT_MODS, 1, int, "dependencies");
333
334 stack_size = STACK_SIZE;
335
336 get_options(argc, argv);
337 if (nfiles == 0) panic("no input files");
338 if (stdlib && libdir == NULL) panic("no libdir specified");
339 if (rtlibdir == NULL) rtlibdir = libdir;
340
341 #define bind(x) def_global(find_symbol(#x), ABS, x, X_SYM)
342
343 bind(GC_BASE); bind(GC_REPEAT); bind(GC_BLOCK);
344 bind(GC_MAP); bind(GC_FLEX); bind(GC_END); bind(GC_POINTER);
345 bind(E_CAST); bind(E_ASSIGN); bind(E_CASE);
346 bind(E_WITH); bind(E_ASSERT); bind(E_RETURN);
347 bind(E_BOUND); bind(E_NULL); bind(E_DIV);
348 bind(E_FDIV); bind(E_STACK); bind(E_GLOB);
349
350 /* First pass -- check for dependencies */
351 scan_files();
352
353 /* Compute needed modules */
354 buf_grow(module);
355 module[nmodules].m_dep = ndeps;
356 trace_imports();
357
358 if (status != 0) return status;
359
360 /* Second pass -- link the modules that are needed */
361 init_linker(outname, interp);
362 load_needed();
363 gen_main();
364 if (rtlibdir != NULL)
365 save_string("LIBDIR", rtlibdir);
366 end_linking();
367
368 if (custom)
369 dump_prims();
370
371 return status;
372 }