comparison lwlink/readfiles.c @ 0:2c24602be78f

Initial import from lwtools 3.0.1 version, with new hand built build system and file reorganization
author lost@l-w.ca
date Wed, 19 Jan 2011 22:27:17 -0700
parents
children 7317fbe024af
comparison
equal deleted inserted replaced
-1:000000000000 0:2c24602be78f
1 /*
2 readfiles.c
3 Copyright © 2009 William Astle
4
5 This file is part of LWLINK.
6
7 LWLINK is free software: you can redistribute it and/or modify it under the
8 terms of the GNU General Public License as published by the Free Software
9 Foundation, either version 3 of the License, or (at your option) any later
10 version.
11
12 This program is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 more details.
16
17 You should have received a copy of the GNU General Public License along with
18 this program. If not, see <http://www.gnu.org/licenses/>.
19
20
21 Reads input files
22
23 */
24
25 #include <argp.h>
26 #include <errno.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "lwlink.h"
32 #include "util.h"
33
34 void read_lwobj16v0(fileinfo_t *fn);
35 void read_lwar1v(fileinfo_t *fn);
36
37 /*
38 The logic of reading the entire file into memory is simple. All the symbol
39 names in the file are NUL terminated strings and can be used directly without
40 making additional copies.
41 */
42 void read_file(fileinfo_t *fn)
43 {
44 if (!memcmp(fn -> filedata, "LWOBJ16", 8))
45 {
46 // read v0 LWOBJ16 file
47 read_lwobj16v0(fn);
48 }
49 else if (!memcmp(fn -> filedata, "LWAR1V", 6))
50 {
51 // archive file
52 read_lwar1v(fn);
53 }
54 else
55 {
56 fprintf(stderr, "%s: unknown file format\n", fn -> filename);
57 exit(1);
58 }
59 }
60
61 void read_files(void)
62 {
63 int i;
64 long size;
65 FILE *f;
66 long bread;
67 for (i = 0; i < ninputfiles; i++)
68 {
69 if (inputfiles[i] -> islib)
70 {
71 char *tf;
72 int s;
73 int j;
74
75 f = NULL;
76
77 for (j = 0; j < nlibdirs; j++)
78 {
79 s = strlen(libdirs[j]) + 7 + strlen(inputfiles[i] -> filename);
80 tf = lw_malloc(s + 1);
81 sprintf(tf, "%s/lib%s.a", libdirs[j], inputfiles[i] -> filename);
82 f = fopen(tf, "rb");
83 if (!f)
84 {
85 free(tf);
86 continue;
87 }
88 free(tf);
89 break;
90 }
91 if (!f)
92 {
93 fprintf(stderr, "Can't open library: -l%s\n", inputfiles[i] -> filename);
94 exit(1);
95 }
96 }
97 else
98 {
99 f = fopen(inputfiles[i] -> filename, "rb");
100 if (!f)
101 {
102 fprintf(stderr, "Can't open file %s:", inputfiles[i] -> filename);
103 perror("");
104 exit(1);
105 }
106 }
107 fseek(f, 0, SEEK_END);
108 size = ftell(f);
109 rewind(f);
110
111 inputfiles[i] -> filedata = lw_malloc(size);
112 inputfiles[i] -> filesize = size;
113
114 bread = fread(inputfiles[i] -> filedata, 1, size, f);
115 if (bread < size)
116 {
117 fprintf(stderr, "Short read on file %s (%ld/%ld):", inputfiles[i] -> filename, bread, size);
118 perror("");
119 exit(1);
120 }
121
122 fclose(f);
123
124 read_file(inputfiles[i]);
125 }
126 }
127
128 // this macro is used to bail out if we run off the end of the file data
129 // while parsing - it keeps the code below cleaner
130 #define NEXTBYTE() do { cc++; if (cc > fn -> filesize) { fprintf(stderr, "%s: invalid file format\n", fn -> filename); exit(1); } } while (0)
131 // this macro is used to refer to the current byte in the stream
132 #define CURBYTE() (fn -> filedata[cc < fn -> filesize ? cc : fn -> filesize - 1])
133 // this one will leave the input pointer past the trailing NUL
134 #define CURSTR() read_lwobj16v0_str(&cc, fn)
135 unsigned char *read_lwobj16v0_str(long *cc1, fileinfo_t *fn)
136 {
137 int cc = *cc1;
138 unsigned char *fp;
139 fp = &CURBYTE();
140 while (CURBYTE())
141 NEXTBYTE();
142 NEXTBYTE();
143 *cc1 = cc;
144 return fp;
145 }
146 // the function below can be switched to dealing with data coming from a
147 // source other than an in-memory byte pool by adjusting the input data
148 // in "fn" and the above two macros
149
150 void read_lwobj16v0(fileinfo_t *fn)
151 {
152 unsigned char *fp;
153 long cc;
154 section_t *s;
155 int val;
156 symtab_t *se;
157
158 // start reading *after* the magic number
159 cc = 8;
160
161 // init data
162 fn -> sections = NULL;
163 fn -> nsections = 0;
164
165 while (1)
166 {
167 // NEXTBYTE();
168 // bail out if no more sections
169 if (!(CURBYTE()))
170 break;
171
172 fp = CURSTR();
173
174 // we now have a section name in fp
175 // create new section entry
176 fn -> sections = lw_realloc(fn -> sections, sizeof(section_t) * (fn -> nsections + 1));
177 s = &(fn -> sections[fn -> nsections]);
178 fn -> nsections += 1;
179
180 s -> localsyms = NULL;
181 s -> flags = 0;
182 s -> codesize = 0;
183 s -> name = fp;
184 s -> loadaddress = 0;
185 s -> localsyms = NULL;
186 s -> exportedsyms = NULL;
187 s -> incompletes = NULL;
188 s -> processed = 0;
189 s -> file = fn;
190
191 // read flags
192 while (CURBYTE())
193 {
194 switch (CURBYTE())
195 {
196 case 0x01:
197 s -> flags |= SECTION_BSS;
198 break;
199
200 default:
201 fprintf(stderr, "%s (%s): unrecognized section flag %02X\n", fn -> filename, s -> name, (int)(CURBYTE()));
202 exit(1);
203 }
204 NEXTBYTE();
205 }
206 // skip NUL terminating flags
207 NEXTBYTE();
208
209 // now parse the local symbol table
210 while (CURBYTE())
211 {
212 fp = CURSTR();
213
214 // fp is the symbol name
215 val = (CURBYTE()) << 8;
216 NEXTBYTE();
217 val |= (CURBYTE());
218 NEXTBYTE();
219 // val is now the symbol value
220
221 // create symbol table entry
222 se = lw_malloc(sizeof(symtab_t));
223 se -> next = s -> localsyms;
224 s -> localsyms = se;
225 se -> sym = fp;
226 se -> offset = val;
227 }
228 // skip terminating NUL
229 NEXTBYTE();
230
231 // now parse the exported symbol table
232 while (CURBYTE())
233 {
234 fp = CURSTR();
235
236 // fp is the symbol name
237 val = (CURBYTE()) << 8;
238 NEXTBYTE();
239 val |= (CURBYTE());
240 NEXTBYTE();
241 // val is now the symbol value
242
243 // create symbol table entry
244 se = lw_malloc(sizeof(symtab_t));
245 se -> next = s -> exportedsyms;
246 s -> exportedsyms = se;
247 se -> sym = fp;
248 se -> offset = val;
249 }
250 // skip terminating NUL
251 NEXTBYTE();
252
253 // now parse the incomplete references and make a list of
254 // external references that need resolution
255 while (CURBYTE())
256 {
257 reloc_t *rp;
258 lw_expr_term_t *term;
259
260 // we have a reference
261 rp = lw_malloc(sizeof(reloc_t));
262 rp -> next = s -> incompletes;
263 s -> incompletes = rp;
264 rp -> offset = 0;
265 rp -> expr = lw_expr_stack_create();
266 rp -> flags = RELOC_NORM;
267
268 // parse the expression
269 while (CURBYTE())
270 {
271 int tt = CURBYTE();
272 NEXTBYTE();
273 switch (tt)
274 {
275 case 0xFF:
276 // a flag specifier
277 tt = CURBYTE();
278 rp -> flags = tt;
279 NEXTBYTE();
280 term = NULL;
281 break;
282
283 case 0x01:
284 // 16 bit integer
285 tt = CURBYTE() << 8;
286 NEXTBYTE();
287 tt |= CURBYTE();
288 NEXTBYTE();
289 // normalize for negatives...
290 if (tt > 0x7fff)
291 tt -= 0x10000;
292 term = lw_expr_term_create_int(tt);
293 break;
294
295 case 0x02:
296 // external symbol reference
297 term = lw_expr_term_create_sym(CURSTR(), 0);
298 break;
299
300 case 0x03:
301 // internal symbol reference
302 term = lw_expr_term_create_sym(CURSTR(), 1);
303 break;
304
305 case 0x04:
306 // operator
307 term = lw_expr_term_create_oper(CURBYTE());
308 NEXTBYTE();
309 break;
310
311 case 0x05:
312 // section base reference (NULL internal reference is
313 // the section base address
314 term = lw_expr_term_create_sym(NULL, 1);
315 break;
316
317 default:
318 fprintf(stderr, "%s (%s): bad relocation expression (%02X)\n", fn -> filename, s -> name, tt);
319 exit(1);
320 }
321 if (term)
322 {
323 lw_expr_stack_push(rp -> expr, term);
324 lw_expr_term_free(term);
325 }
326 }
327 // skip the NUL
328 NEXTBYTE();
329
330 // fetch the offset
331 rp -> offset = CURBYTE() << 8;
332 NEXTBYTE();
333 rp -> offset |= CURBYTE() & 0xff;
334 NEXTBYTE();
335 }
336 // skip the NUL terminating the relocations
337 NEXTBYTE();
338
339 // now set code location and size and verify that the file
340 // contains data going to the end of the code (if !SECTION_BSS)
341 s -> codesize = CURBYTE() << 8;
342 NEXTBYTE();
343 s -> codesize |= CURBYTE();
344 NEXTBYTE();
345
346 s -> code = &(CURBYTE());
347
348 // skip the code if we're not in a BSS section
349 if (!(s -> flags & SECTION_BSS))
350 {
351 int i;
352 for (i = 0; i < s -> codesize; i++)
353 NEXTBYTE();
354 }
355 }
356 }
357
358 /*
359 Read an archive file - this will create a "sub" record and farm out the
360 parsing of the sub files to the regular file parsers
361
362 The archive file format consists of the 6 byte magic number followed by a
363 series of records as follows:
364
365 - NUL terminated file name
366 - 32 bit file length in big endian order
367 - the file data
368
369 An empty file name indicates the end of the file.
370
371 */
372 void read_lwar1v(fileinfo_t *fn)
373 {
374 unsigned long cc = 6;
375 unsigned long flen;
376 unsigned long l;
377 for (;;)
378 {
379 if (cc >= fn -> filesize || !(fn -> filedata[cc]))
380 return;
381
382 for (l = cc; cc < fn -> filesize && fn -> filedata[cc]; cc++)
383 /* do nothing */ ;
384
385 cc++;
386
387 if (cc >= fn -> filesize)
388 {
389 fprintf(stderr, "Malformed archive file %s.\n", fn -> filename);
390 exit(1);
391 }
392
393 if (cc + 4 > fn -> filesize)
394 return;
395
396 flen = (fn -> filedata[cc++] << 24);
397 flen |= (fn -> filedata[cc++] << 16);
398 flen |= (fn -> filedata[cc++] << 8);
399 flen |= (fn -> filedata[cc++]);
400
401 if (flen == 0)
402 return;
403
404 if (cc + flen > fn -> filesize)
405 {
406 fprintf(stderr, "Malformed archive file %s.\n", fn -> filename);
407 exit(1);
408 }
409
410 // add the "sub" input file
411 fn -> subs = lw_realloc(fn -> subs, sizeof(fileinfo_t *) * (fn -> nsubs + 1));
412 fn -> subs[fn -> nsubs] = lw_malloc(sizeof(fileinfo_t));
413 memset(fn -> subs[fn -> nsubs], 0, sizeof(fileinfo_t));
414 fn -> subs[fn -> nsubs] -> filedata = fn -> filedata + cc;
415 fn -> subs[fn -> nsubs] -> filesize = flen;
416 fn -> subs[fn -> nsubs] -> filename = lw_strdup(fn -> filedata + l);
417 fn -> subs[fn -> nsubs] -> parent = fn;
418 fn -> subs[fn -> nsubs] -> forced = fn -> forced;
419 read_file(fn -> subs[fn -> nsubs]);
420 fn -> nsubs++;
421 cc += flen;
422 }
423 }