1 /*
2 Z88DK Z80 Macro Assembler
3
4 Copyright (C) Gunther Strube, InterLogic 1993-99
5 Copyright (C) Paulo Custodio, 2011-2020
6 License: The Artistic License 2.0, http://www.perlfoundation.org/artistic_license_2_0
7 Repository: https://github.com/z88dk/z88dk
8
9 Handle object file contruction, reading and writing
10 */
11
12 #include "class.h"
13 #include "codearea.h"
14 #include "die.h"
15 #include "errors.h"
16 #include "fileutil.h"
17 #include "libfile.h"
18 #include "model.h"
19 #include "options.h"
20 #include "str.h"
21 #include "strutil.h"
22 #include "utstring.h"
23 #include "zobjfile.h"
24 #include "zutils.h"
25
26 /*-----------------------------------------------------------------------------
27 * Object header
28 *----------------------------------------------------------------------------*/
29 char Z80objhdr[] = "Z80RMF" OBJ_VERSION;
30
31 #define Z80objhdr_size (sizeof(Z80objhdr)-1)
32 #define Z80objhdr_version_pos 6
33
34 /*-----------------------------------------------------------------------------
35 * Write module to object file
36 *----------------------------------------------------------------------------*/
write_expr(FILE * fp)37 static long write_expr(FILE* fp)
38 {
39 STR_DEFINE(last_sourcefile, STR_SIZE); /* keep last source file referred to in object */
40 ExprListElem* iter;
41 Expr* expr;
42 char range;
43 const char* target_name;
44 long expr_ptr;
45
46 if (ExprList_empty(CURRENTMODULE->exprs)) /* no expressions */
47 return -1;
48
49 expr_ptr = ftell(fp);
50 for (iter = ExprList_first(CURRENTMODULE->exprs); iter != NULL; iter = ExprList_next(iter))
51 {
52 expr = iter->obj;
53
54 /* store range */
55 range = 0;
56 if (expr->target_name)
57 {
58 target_name = expr->target_name; /* EQU expression */
59 range = '=';
60 }
61 else
62 {
63 target_name = ""; /* patch expression */
64 switch (expr->range)
65 {
66 case RANGE_DWORD: range = 'L'; break;
67 case RANGE_WORD: range = 'C'; break;
68 case RANGE_WORD_BE: range = 'B'; break;
69 case RANGE_BYTE_UNSIGNED: range = 'U'; break;
70 case RANGE_BYTE_SIGNED: range = 'S'; break;
71 case RANGE_BYTE_TO_WORD_UNSIGNED: range = 'u'; break;
72 case RANGE_BYTE_TO_WORD_SIGNED: range = 's'; break;
73 case RANGE_PTR24: range = 'P'; break;
74 case RANGE_JR_OFFSET: range = 'J'; break;
75 default: xassert(0);
76 }
77 }
78 xfwrite_byte(range, fp); /* range of expression */
79
80 /* store file name if different from last, folowed by source line number */
81 if (expr->filename != NULL &&
82 strcmp(Str_data(last_sourcefile), expr->filename) != 0)
83 {
84 xfwrite_wcount_cstr(expr->filename, fp);
85 Str_set(last_sourcefile, expr->filename);
86 }
87 else
88 xfwrite_wcount_cstr("", fp);
89
90 xfwrite_dword(expr->line_nr, fp); /* source line number */
91
92 xfwrite_bcount_cstr(expr->section->name, fp); /* section name */
93
94 xfwrite_word(expr->asmpc, fp); /* ASMPC */
95 xfwrite_word(expr->code_pos, fp); /* patchptr */
96 xfwrite_bcount_cstr(target_name, fp); /* target symbol for expression */
97 xfwrite_wcount_cstr(Str_data(expr->text), fp); /* expression */
98 }
99
100 xfwrite_byte(0, fp); /* terminator */
101
102 STR_DELETE(last_sourcefile);
103
104 return expr_ptr;
105 }
106
write_symbols_symtab(FILE * fp,SymbolHash * symtab)107 static int write_symbols_symtab(FILE* fp, SymbolHash* symtab)
108 {
109 SymbolHashElem* iter;
110 Symbol* sym;
111 int written = 0;
112 char scope, type;
113
114 for (iter = SymbolHash_first(symtab); iter; iter = SymbolHash_next(iter))
115 {
116 sym = (Symbol*)iter->value;
117
118 /* scope */
119 scope =
120 (sym->scope == SCOPE_PUBLIC || (sym->is_defined && sym->scope == SCOPE_GLOBAL)) ? 'G' :
121 (sym->scope == SCOPE_LOCAL) ? 'L' : 0;
122
123 if (scope != 0 && sym->is_touched && sym->type != TYPE_UNKNOWN)
124 {
125 /* type */
126 type = 0;
127 switch (sym->type)
128 {
129 case TYPE_CONSTANT: type = 'C'; break;
130 case TYPE_ADDRESS: type = 'A'; break;
131 case TYPE_COMPUTED: type = '='; break;
132 default: xassert(0);
133 }
134
135 xfwrite_byte(scope, fp);
136 xfwrite_byte(type, fp);
137
138 xfwrite_bcount_cstr(sym->section->name, fp);
139 xfwrite_dword(sym->value, fp);
140 xfwrite_bcount_cstr(sym->name, fp);
141
142 // write symbol definition location
143 xfwrite_bcount_cstr(sym->filename ? sym->filename : "", fp);
144 xfwrite_dword(sym->line_nr, fp);
145
146 written++;
147 }
148 }
149 return written;
150 }
151
write_symbols(FILE * fp)152 static long write_symbols(FILE* fp)
153 {
154 long symbols_ptr;
155 int written = 0;
156
157 symbols_ptr = ftell(fp);
158
159 written += write_symbols_symtab(fp, CURRENTMODULE->local_symtab);
160 written += write_symbols_symtab(fp, global_symtab);
161
162 if (written)
163 {
164 xfwrite_byte(0, fp); /* terminator */
165 return symbols_ptr;
166 }
167 else
168 return -1;
169 }
170
write_externsym(FILE * fp)171 static long write_externsym(FILE* fp)
172 {
173 SymbolHashElem* iter;
174 Symbol* sym;
175 long externsym_ptr;
176 int written = 0;
177
178 externsym_ptr = ftell(fp);
179
180 for (iter = SymbolHash_first(global_symtab); iter; iter = SymbolHash_next(iter))
181 {
182 sym = (Symbol*)iter->value;
183
184 if (sym->is_touched &&
185 (sym->scope == SCOPE_EXTERN || (!sym->is_defined && sym->scope == SCOPE_GLOBAL)))
186 {
187 xfwrite_bcount_cstr(sym->name, fp);
188 written++;
189 }
190 }
191
192 if (written)
193 return externsym_ptr;
194 else
195 return -1;
196 }
197
write_modname(FILE * fp)198 static long write_modname(FILE* fp)
199 {
200 long modname_ptr = ftell(fp);
201 xfwrite_bcount_cstr(CURRENTMODULE->modname, fp); /* write module name */
202 return modname_ptr;
203 }
204
write_code(FILE * fp)205 static long write_code(FILE* fp)
206 {
207 long code_ptr;
208 int code_size = 0;
209 bool wrote_data = false;
210
211 code_ptr = ftell(fp);
212 wrote_data = fwrite_module_code(fp, &code_size);
213
214 if (opts.verbose)
215 printf("Module '%s' size: %ld bytes\n", CURRENTMODULE->modname, (long)code_size);
216
217 if (wrote_data)
218 return code_ptr;
219 else
220 return -1;
221 }
222
write_obj_file(const char * source_filename)223 void write_obj_file(const char* source_filename)
224 {
225 const char* obj_filename;
226 FILE* fp;
227 long header_ptr, modname_ptr, expr_ptr, symbols_ptr, externsym_ptr, code_ptr;
228 int i;
229
230 /* open file */
231 obj_filename = get_obj_filename(source_filename);
232
233 if (opts.verbose)
234 printf("Writing object file '%s'\n", path_canon(obj_filename));
235
236 fp = xfopen(obj_filename, "wb");
237
238 /* write header */
239 xfwrite_cstr(Z80objhdr, fp);
240
241 /* write placeholders for 5 pointers */
242 header_ptr = ftell(fp);
243 for (i = 0; i < 5; i++)
244 xfwrite_dword(-1, fp);
245
246 /* write sections, return pointers */
247 expr_ptr = write_expr(fp);
248 symbols_ptr = write_symbols(fp);
249 externsym_ptr = write_externsym(fp);
250 modname_ptr = write_modname(fp);
251 code_ptr = write_code(fp);
252
253 /* write pointers to areas */
254 fseek(fp, header_ptr, SEEK_SET);
255 xfwrite_dword(modname_ptr, fp);
256 xfwrite_dword(expr_ptr, fp);
257 xfwrite_dword(symbols_ptr, fp);
258 xfwrite_dword(externsym_ptr, fp);
259 xfwrite_dword(code_ptr, fp);
260
261 /* close temp file and rename to object file */
262 xfclose(fp);
263 }
264
265
266
267 /*-----------------------------------------------------------------------------
268 * Check the object file header
269 *----------------------------------------------------------------------------*/
test_header(FILE * file)270 static bool test_header(FILE* file)
271 {
272 char buffer[Z80objhdr_size];
273
274 if (fread(buffer, 1, Z80objhdr_size, file) == Z80objhdr_size &&
275 memcmp(buffer, Z80objhdr, Z80objhdr_size) == 0
276 )
277 return true;
278 else
279 return false;
280 }
281
282 /*-----------------------------------------------------------------------------
283 * Object file class
284 *----------------------------------------------------------------------------*/
285 DEF_CLASS(OFile);
286
OFile_init(OFile * self)287 void OFile_init(OFile* self)
288 {
289 self->modname_ptr =
290 self->expr_ptr =
291 self->symbols_ptr =
292 self->externsym_ptr =
293 self->code_ptr = -1;
294 }
295
OFile_copy(OFile * self,OFile * other)296 void OFile_copy(OFile* self, OFile* other) { xassert(0); }
297
OFile_fini(OFile * self)298 void OFile_fini(OFile* self)
299 {
300 /* if not from library, close file */
301 if (self->file != NULL &&
302 self->start_ptr == 0
303 )
304 xfclose(self->file);
305
306 /* if writing but not closed, delete partialy created file */
307 if (self->writing &&
308 self->start_ptr == 0 &&
309 self->file != NULL &&
310 self->filename != NULL
311 )
312 remove(self->filename);
313 }
314
315 /*-----------------------------------------------------------------------------
316 * read object file header from within an open library file.
317 * Return NULL if invalid object file or not the correct version.
318 * Object needs to be deleted by caller by OBJ_DELETE()
319 * Keeps the library file open
320 *----------------------------------------------------------------------------*/
OFile_read_header(FILE * file,size_t start_ptr)321 OFile* OFile_read_header(FILE* file, size_t start_ptr)
322 {
323 UT_string* modname = utstr_new();
324 OFile* self;
325
326 /* check file version */
327 fseek(file, start_ptr, SEEK_SET);
328 if (!test_header(file))
329 return NULL;
330
331 /* create OFile object */
332 self = OBJ_NEW(OFile);
333
334 self->file = file;
335 self->start_ptr = start_ptr;
336 self->writing = false;
337
338 self->modname_ptr = xfread_dword(file);
339 self->expr_ptr = xfread_dword(file);
340 self->symbols_ptr = xfread_dword(file);
341 self->externsym_ptr = xfread_dword(file);
342 self->code_ptr = xfread_dword(file);
343
344 /* read module name */
345 fseek(file, start_ptr + self->modname_ptr, SEEK_SET);
346 xfread_bcount_str(modname, file);
347 self->modname = spool_add(utstr_body(modname));
348
349 utstr_free(modname);
350
351 return self;
352 }
353
354 /*-----------------------------------------------------------------------------
355 * open object file for reading, read header.
356 * Return NULL if invalid object file or not the correct version.
357 * Object needs to be deleted by caller by OBJ_DELETE()
358 * Keeps the object file open
359 *----------------------------------------------------------------------------*/
_OFile_open_read(const char * filename,bool test_mode)360 static OFile* _OFile_open_read(const char* filename, bool test_mode)
361 {
362 OFile* self;
363 FILE* file;
364
365 /* file exists? */
366 file = fopen(filename, "rb");
367 if (!file) {
368 if (!test_mode)
369 error_read_file(filename);
370 return NULL;
371 }
372
373 /* read header */
374 self = OFile_read_header(file, 0);
375 if (self == NULL)
376 {
377 xfclose(file);
378
379 if (!test_mode)
380 error_not_obj_file(filename);
381
382 return NULL;
383 }
384 self->filename = spool_add(filename);
385
386 /* return object */
387 return self;
388 }
389
OFile_open_read(const char * filename)390 OFile* OFile_open_read(const char* filename)
391 {
392 return _OFile_open_read(filename, false);
393 }
394
395 /*-----------------------------------------------------------------------------
396 * close object file
397 *----------------------------------------------------------------------------*/
OFile_close(OFile * self)398 void OFile_close(OFile* self)
399 {
400 if (self != NULL && self->file != NULL)
401 {
402 xfclose(self->file);
403 self->file = NULL;
404 }
405 }
406
407 /*-----------------------------------------------------------------------------
408 * test if a object file exists and is the correct version, return object if yes
409 * return NULL if not.
410 * Object needs to be deleted by caller by OBJ_DELETE()
411 *----------------------------------------------------------------------------*/
OFile_test_file(const char * filename)412 OFile* OFile_test_file(const char* filename)
413 {
414 return _OFile_open_read(filename, true);
415 }
416
417 /*-----------------------------------------------------------------------------
418 * return static ByteArray with binary contents of given file
419 * return NULL if input file is not an object, or does not exist
420 * NOTE: not reentrant, reuses array on each call
421 *----------------------------------------------------------------------------*/
read_obj_file_data(const char * filename)422 ByteArray* read_obj_file_data(const char* filename)
423 {
424 static ByteArray* buffer = NULL;
425 size_t size;
426 OFile* ofile;
427
428 /* static object to read each file, not reentrant */
429 INIT_OBJ(ByteArray, &buffer);
430
431 /* open object file, check header */
432 ofile = OFile_open_read(filename);
433 if (ofile == NULL)
434 return NULL; /* error */
435
436 fseek(ofile->file, 0, SEEK_END); /* file pointer to end of file */
437 size = ftell(ofile->file);
438 fseek(ofile->file, 0, SEEK_SET); /* file pointer to start of file */
439
440 /* set array size, read file */
441 ByteArray_set_size(buffer, size);
442 xfread_bytes(ByteArray_item(buffer, 0), size, ofile->file);
443
444 OBJ_DELETE(ofile);
445
446 return buffer;
447 }
448
449 /*-----------------------------------------------------------------------------
450 * Updates current module name and size, if given object file is valid
451 * Load module name and size, when assembling with -d and up-to-date
452 *----------------------------------------------------------------------------*/
objmodule_loaded_1(const char * obj_filename,UT_string * section_name)453 static bool objmodule_loaded_1(const char* obj_filename, UT_string* section_name)
454 {
455 int code_size;
456 OFile* ofile;
457 Section* section;
458
459 ofile = OFile_test_file(obj_filename);
460 if (ofile != NULL)
461 {
462 CURRENTMODULE->modname = ofile->modname;
463
464 /* reserve space in each section; BUG_0015 */
465 if (ofile->code_ptr >= 0)
466 {
467 fseek(ofile->file, ofile->start_ptr + ofile->code_ptr, SEEK_SET);
468
469 while (true) /* read sections until end marker */
470 {
471 code_size = xfread_dword(ofile->file);
472 if (code_size < 0)
473 break;
474
475 /* reserve space in section */
476 xfread_bcount_str(section_name, ofile->file);
477 section = new_section(utstr_body(section_name));
478 read_origin(ofile->file, section);
479 section->align = xfread_dword(ofile->file);
480
481 append_reserve(code_size);
482
483 /* advance past code block */
484 fseek(ofile->file, code_size, SEEK_CUR);
485 }
486 }
487
488 OBJ_DELETE(ofile); /* BUG_0049 */
489
490 return true;
491 }
492 else
493 return false;
494 }
495
objmodule_loaded(const char * obj_filename)496 bool objmodule_loaded(const char* obj_filename)
497 {
498 UT_string* section_name = utstr_new();
499 bool ret = objmodule_loaded_1(obj_filename, section_name);
500 utstr_free(section_name);
501 return ret;
502 }
503
check_object_file(const char * obj_filename)504 bool check_object_file(const char* obj_filename)
505 {
506 return check_obj_lib_file(
507 obj_filename,
508 Z80objhdr,
509 error_not_obj_file,
510 error_obj_file_version);
511 }
512
no_error_file(const char * filename)513 static void no_error_file(const char* filename) {}
no_error_version(const char * filename,int version,int expected)514 static void no_error_version(const char* filename, int version, int expected) {}
515
check_object_file_no_errors(const char * obj_filename)516 bool check_object_file_no_errors(const char* obj_filename) {
517 return check_obj_lib_file(
518 obj_filename,
519 Z80objhdr,
520 no_error_file,
521 no_error_version);
522 }
523
check_obj_lib_file(const char * filename,char * signature,void (* error_file)(const char *),void (* error_version)(const char *,int,int))524 bool check_obj_lib_file(const char* filename,
525 char* signature,
526 void(*error_file)(const char*),
527 void(*error_version)(const char*, int, int))
528 {
529 FILE* fp = NULL;
530
531 // can read file?
532 fp = fopen(filename, "rb");
533 if (fp == NULL) {
534 error_read_file(filename);
535 goto error;
536 }
537
538 // can read header?
539 char header[Z80objhdr_size + 1];
540 if (Z80objhdr_size != fread(header, 1, Z80objhdr_size, fp)) {
541 error_file(filename);
542 goto error;
543 }
544
545 // header has correct prefix?
546 if (strncmp(header, signature, Z80objhdr_version_pos) != 0) {
547 error_file(filename);
548 goto error;
549 }
550
551 // has right version?
552 header[Z80objhdr_size] = '\0';
553 int version, expected;
554 sscanf(OBJ_VERSION, "%d", &expected);
555 if (1 != sscanf(header + Z80objhdr_version_pos, "%d", &version)) {
556 error_file(filename);
557 goto error;
558 }
559 if (version != expected) {
560 error_version(filename, version, expected);
561 goto error;
562 }
563
564 // ok
565 fclose(fp);
566 return true;
567
568 error:
569 if (fp)
570 fclose(fp);
571 return false;
572 }
573