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