1 /*
2  * Hatari - symbols.c
3  *
4  * Copyright (C) 2010-2014 by Eero Tamminen
5  *
6  * This file is distributed under the GNU General Public License, version 2
7  * or at your option any later version. Read the file gpl.txt for details.
8  *
9  * symbols.c - Hatari debugger symbol/address handling; parsing, sorting,
10  * matching, TAB completion support etc.
11  *
12  * Symbol/address information is read either from:
13  * - A program file's DRI/GST format symbol table, or
14  * - ASCII file which contents are subset of "nm" output i.e. composed of
15  *   a hexadecimal addresses followed by a space, letter indicating symbol
16  *   type (T = text/code, D = data, B = BSS), space and the symbol name.
17  *   Empty lines and lines starting with '#' are ignored.  It's AHCC SYM
18  *   output compatible.
19  */
20 const char Symbols_fileid[] = "Hatari symbols.c : " __DATE__ " " __TIME__;
21 
22 #include <ctype.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <assert.h>
26 #include <SDL_types.h>
27 #include <SDL_endian.h>
28 #include "main.h"
29 #include "symbols.h"
30 #include "debugui.h"
31 #include "debug_priv.h"
32 #include "debugInfo.h"
33 #include "evaluate.h"
34 
35 typedef struct {
36 	char *name;
37 	Uint32 address;
38 	symtype_t type;
39 } symbol_t;
40 
41 typedef struct {
42 	int count;		/* final symbol count */
43 	int symbols;		/* initial symbol count */
44 	symbol_t *addresses;	/* items sorted by address */
45 	symbol_t *names;	/* items sorted by symbol name */
46 } symbol_list_t;
47 
48 typedef struct {
49 	Uint32 offset;
50 	Uint32 end;
51 } prg_section_t;
52 
53 
54 /* how many characters the symbol name can have.
55  * NOTE: change also sscanf width arg if you change this!!!
56  */
57 #define MAX_SYM_SIZE 32
58 
59 
60 /* TODO: add symbol name/address file names to configuration? */
61 static symbol_list_t *CpuSymbolsList;
62 static symbol_list_t *DspSymbolsList;
63 
64 /* path for last loaded program (through GEMDOS HD emulation) */
65 static char *CurrentProgramPath;
66 static bool SymbolsAreForProgram;
67 static bool AutoLoadFailed;
68 
69 
70 /* ------------------ load and free functions ------------------ */
71 
72 /**
73  * compare function for qsort() to sort according to symbol address
74  */
symbols_by_address(const void * s1,const void * s2)75 static int symbols_by_address(const void *s1, const void *s2)
76 {
77 	Uint32 addr1 = ((const symbol_t*)s1)->address;
78 	Uint32 addr2 = ((const symbol_t*)s2)->address;
79 
80 	if (addr1 < addr2) {
81 		return -1;
82 	}
83 	if (addr1 > addr2) {
84 		return 1;
85 	}
86 	fprintf(stderr, "WARNING: symbols '%s' & '%s' have the same 0x%x address.\n",
87 		((const symbol_t*)s1)->name, ((const symbol_t*)s2)->name, addr1);
88 	return 0;
89 }
90 
91 /**
92  * compare function for qsort() to sort according to symbol name
93  */
symbols_by_name(const void * s1,const void * s2)94 static int symbols_by_name(const void *s1, const void *s2)
95 {
96 	const char* name1 = ((const symbol_t*)s1)->name;
97 	const char* name2 = ((const symbol_t*)s2)->name;
98 	int ret;
99 
100 	ret = strcmp(name1, name2);
101 	if (!ret) {
102 		fprintf(stderr, "WARNING: addresses 0x%x & 0x%x have the same '%s' name.\n",
103 			((const symbol_t*)s1)->address, ((const symbol_t*)s2)->address, name1);
104 	}
105 	return ret;
106 }
107 
108 
109 /**
110  * Allocate symbol list & names for given number of items.
111  * Return allocated list or NULL on failure.
112  */
symbol_list_alloc(int symbols)113 static symbol_list_t* symbol_list_alloc(int symbols)
114 {
115 	symbol_list_t *list;
116 
117 	if (!symbols) {
118 		return NULL;
119 	}
120 	list = calloc(1, sizeof(symbol_list_t));
121 	if (list) {
122 		list->names = malloc(symbols * sizeof(symbol_t));
123 		if (!list->names) {
124 			free(list);
125 			list = NULL;
126 		}
127 	}
128 	return list;
129 }
130 
131 /**
132  * Free symbol list & names.
133  */
symbol_list_free(symbol_list_t * list)134 static void symbol_list_free(symbol_list_t *list)
135 {
136 	if (list) {
137 		if (list->names) {
138 			free(list->names);
139 		}
140 		free(list);
141 	}
142 }
143 
144 /**
145  * Return symbol type identifier char
146  */
symbol_char(int type)147 static char symbol_char(int type)
148 {
149 	switch (type) {
150 	case SYMTYPE_TEXT: return 'T';
151 	case SYMTYPE_DATA: return 'D';
152 	case SYMTYPE_BSS:  return 'B';
153 	default: return '?';
154 	}
155 }
156 
157 #define INVALID_SYMBOL_OFFSETS ((symbol_list_t*)1)
158 
159 /**
160  * Load symbols of given type and the symbol address addresses from
161  * DRI/GST format symbol table, and add given offsets to the addresses:
162  *	http://toshyp.atari.org/en/005005.html
163  * Return symbols list or NULL for failure.
164  */
symbols_load_dri(FILE * fp,prg_section_t * sections,symtype_t gettype,Uint32 tablesize)165 static symbol_list_t* symbols_load_dri(FILE *fp, prg_section_t *sections, symtype_t gettype, Uint32 tablesize)
166 {
167 	int i, count, symbols, len, outside;
168 	int dtypes, locals, ofiles;
169 	prg_section_t *section;
170 	symbol_list_t *list;
171 	symtype_t symtype;
172 #define DRI_ENTRY_SIZE	14
173 	char name[23];
174 	Uint16 symid;
175 	Uint32 address;
176 
177 	if (tablesize % DRI_ENTRY_SIZE || !tablesize) {
178 		fprintf(stderr, "ERROR: invalid DRI/GST symbol table size %d!\n", tablesize);
179 		return NULL;
180 	}
181 	symbols = tablesize / DRI_ENTRY_SIZE;
182 	if (!(list = symbol_list_alloc(symbols))) {
183 		return NULL;
184 	}
185 
186 	outside = dtypes = ofiles = locals = count = 0;
187 	for (i = 1; i <= symbols; i++) {
188 		/* read DRI symbol table slot */
189 		if (fread(name, 8, 1, fp) != 1 ||
190 		    fread(&symid, sizeof(symid), 1, fp) != 1 ||
191 		    fread(&address, sizeof(address), 1, fp) != 1) {
192 			break;
193 		}
194 		address = SDL_SwapBE32(address);
195 		symid = SDL_SwapBE16(symid);
196 
197 		/* GST extended DRI symbol format? */
198 		if ((symid & 0x0048)) {
199 			/* next slot is rest of name */
200 			i += 1;
201 			if (fread(name+8, 14, 1, fp) != 1) {
202 				break;
203 			}
204 			name[22] = '\0';
205 		} else {
206 			name[8] = '\0';
207 		}
208 
209 		/* check section */
210 		switch (symid & 0xf00) {
211 		case 0x0200:
212 			symtype = SYMTYPE_TEXT;
213 			section = &(sections[0]);
214 			break;
215 		case 0x0400:
216 			symtype = SYMTYPE_DATA;
217 			section = &(sections[1]);
218 			break;
219 		case 0x0100:
220 			symtype = SYMTYPE_BSS;
221 			section = &(sections[2]);
222 			break;
223 		default:
224 			if ((symid & 0xe000) == 0xe000) {
225 				dtypes++;
226 				continue;
227 			}
228 			fprintf(stderr, "WARNING: ignoring symbol '%s' in slot %d of unknown type 0x%x.\n", name, i, symid);
229 			continue;
230 		}
231 		if (!(gettype & symtype)) {
232 			continue;
233 		}
234 		if (name[0] == '.' && name[1] == 'L') {
235 			locals++;
236 			continue;
237 		}
238 		len = strlen(name);
239 		if (strchr(name, '/') || (len > 2 && name[len-2] == '.' && name[len-1] == 'o')) {
240 			ofiles++;
241 			continue;
242 		}
243 		address += section->offset;
244 		if (address > section->end) {
245 			/* VBCC has 1 symbol outside of its section */
246 			if (++outside > 2) {
247 				/* potentially buggy version of VBCC vlink used */
248 				fprintf(stderr, "ERROR: too many invalid offsets, skipping rest of symbols!\n");
249 				symbol_list_free(list);
250 				return INVALID_SYMBOL_OFFSETS;
251 			}
252 			fprintf(stderr, "WARNING: ignoring symbol '%s' of %c type in slot %d with invalid offset 0x%x (>= 0x%x).\n",
253 				name, symbol_char(symtype), i, address, section->end);
254 			continue;
255 		}
256 		list->names[count].address = address;
257 		list->names[count].type = symtype;
258 		list->names[count].name = strdup(name);
259 		assert(list->names[count].name);
260 		count++;
261 	}
262 	if (i <= symbols) {
263 		perror("ERROR: reading symbol failed");
264 		symbol_list_free(list);
265 		return NULL;
266 	}
267 	if (dtypes) {
268 		fprintf(stderr, "NOTE: ignored %d globally defined equated values.\n", dtypes);
269 	}
270 	if (locals) {
271 		fprintf(stderr, "NOTE: ignored %d unnamed / local symbols (= name starts with '.L').\n", locals);
272 	}
273 	if (ofiles) {
274 		/* object file path names most likely get truncated and
275 		 * as result cause unnecessary symbol name conflicts in
276 		 * addition to object file addresses conflicting with
277 		 * first symbol in the object file.
278 		 */
279 		fprintf(stderr, "NOTE: ignored %d object file names (= name has '/' or ends in '.o').\n", ofiles);
280 	}
281 	list->symbols = symbols;
282 	list->count = count;
283 	return list;
284 }
285 
286 /**
287  * Parse program header and use symbol table format specific loader
288  * loader function to load the symbols.
289  * Return symbols list or NULL for failure.
290  */
symbols_load_binary(FILE * fp,symtype_t gettype)291 static symbol_list_t* symbols_load_binary(FILE *fp, symtype_t gettype)
292 {
293 	Uint32 textlen, datalen, bsslen, start, tablesize, tabletype, prgflags;
294 	prg_section_t sections[3];
295 	int offset, reads = 0;
296 	Uint16 relocflag;
297 	const char *info;
298 	symbol_list_t* symbols;
299 
300 	/* get TEXT, DATA & BSS section sizes */
301 	fseek(fp, 2, SEEK_SET);
302 	reads += fread(&textlen, sizeof(textlen), 1, fp);
303 	textlen = SDL_SwapBE32(textlen);
304 	reads += fread(&datalen, sizeof(datalen), 1, fp);
305 	datalen = SDL_SwapBE32(datalen);
306 	reads += fread(&bsslen, sizeof(bsslen), 1, fp);
307 	bsslen = SDL_SwapBE32(bsslen);
308 
309 	/* get symbol table size & type and check that all reads succeeded */
310 	reads += fread(&tablesize, sizeof(tablesize), 1, fp);
311 	tablesize = SDL_SwapBE32(tablesize);
312 	if (!tablesize) {
313 		fprintf(stderr, "ERROR: symbol table missing from the program!\n");
314 		return NULL;
315 	}
316 	reads += fread(&tabletype, sizeof(tabletype), 1, fp);
317 	tabletype = SDL_SwapBE32(tabletype);
318 
319 	/* get program header and whether there's reloc table */
320 	reads += fread(&prgflags, sizeof(prgflags), 1, fp);
321 	prgflags = SDL_SwapBE32(prgflags);
322 	reads += fread(&relocflag, sizeof(relocflag), 1, fp);
323 	relocflag = SDL_SwapBE32(relocflag);
324 
325 	if (reads != 7) {
326 		fprintf(stderr, "ERROR: program header reading failed!\n");
327 		return NULL;
328 	}
329 
330 	/* offsets & max sizes for running program TEXT/DATA/BSS section symbols */
331 	start = DebugInfo_GetTEXT();
332 	if (!start) {
333 		fprintf(stderr, "ERROR: no valid program basepage!\n");
334 		return NULL;
335 	}
336 	sections[0].offset = start;
337 	sections[0].end = start + textlen;
338 	if (DebugInfo_GetTEXTEnd() != sections[0].end - 1) {
339 		fprintf(stderr, "ERROR: given program TEXT section size differs from one in RAM!\n");
340 		return NULL;
341 	}
342 
343 	start = DebugInfo_GetDATA();
344 	sections[1].offset = start;
345 	sections[1].end = start + datalen - 1;
346 
347 	start = DebugInfo_GetBSS();
348 	sections[2].offset = start;
349 	sections[2].end = start + bsslen - 1;
350 
351 	/* go to start of symbol table */
352 	offset = 0x1C + textlen + datalen;
353 	if (fseek(fp, offset, SEEK_SET) < 0) {
354 		perror("ERROR: seeking to symbol table failed");
355 		return NULL;
356 	}
357 	switch (tabletype) {
358 	case 0x4D694E54:	/* "MiNT" */
359 		info = "GCC/MiNT executable, GST symbol table.";
360 		break;
361 	case 0x0:
362 		info = "TOS executable, DRI / GST symbol table.";
363 		break;
364 	default:
365 		fprintf(stderr, "ERROR: unknown executable type 0x%x at offset 0x%x!\n", tabletype, offset);
366 		return NULL;
367 	}
368 	fprintf(stderr, "0x%x program flags, reloc=%d, %s\n", prgflags, relocflag, info);
369 	fprintf(stderr, "Trying to load symbol table at offset 0x%x...\n", offset);
370 	symbols = symbols_load_dri(fp, sections, gettype, tablesize);
371 
372 	if (symbols == INVALID_SYMBOL_OFFSETS && fseek(fp, offset, SEEK_SET) == 0) {
373 		fprintf(stderr, "Re-trying with TEXT-relative BSS/DATA section offsets...\n");
374 		start = DebugInfo_GetTEXT();
375 		sections[1].offset = start;
376 		sections[2].offset = start;
377 		sections[1].end += textlen;
378 		sections[2].end += (textlen + datalen);
379 		symbols = symbols_load_dri(fp, sections, gettype, tablesize);
380 	}
381 	if (symbols == INVALID_SYMBOL_OFFSETS) {
382 		return NULL;
383 	}
384 	return symbols;
385 }
386 
387 /**
388  * Load symbols of given type and the symbol address addresses from
389  * the given ASCII file and add given offsets to the addresses.
390  * Return symbols list or NULL for failure.
391  */
symbols_load_ascii(FILE * fp,Uint32 * offsets,Uint32 maxaddr,symtype_t gettype)392 static symbol_list_t* symbols_load_ascii(FILE *fp, Uint32 *offsets, Uint32 maxaddr, symtype_t gettype)
393 {
394 	symbol_list_t *list;
395 	char symchar, buffer[128], name[MAX_SYM_SIZE+1], *buf;
396 	int count, line, symbols;
397 	Uint32 address, offset;
398 	symtype_t symtype;
399 
400 	/* count content lines */
401 	symbols = 0;
402 	while (fgets(buffer, sizeof(buffer), fp)) {
403 		/* skip comments (AHCC SYM file comments start with '*') */
404 		if (*buffer == '#' || *buffer == '*') {
405 			continue;
406 		}
407 		/* skip empty lines */
408 		for (buf = buffer; isspace((unsigned char)*buf); buf++);
409 		if (!*buf) {
410 			continue;
411 		}
412 		symbols++;
413 	}
414 	if (!symbols) {
415 		fprintf(stderr, "ERROR: no symbols.\n");
416 	}
417 
418 	fseek(fp, 0, SEEK_SET);
419 
420 	/* allocate space for symbol list & names */
421 	if (!(list = symbol_list_alloc(symbols))) {
422 		return NULL;
423 	}
424 
425 	/* read symbols */
426 	count = 0;
427 	for (line = 1; fgets(buffer, sizeof(buffer), fp); line++) {
428 		/* skip comments (AHCC SYM file comments start with '*') */
429 		if (*buffer == '#' || *buffer == '*') {
430 			continue;
431 		}
432 		/* skip empty lines */
433 		for (buf = buffer; isspace((unsigned char)*buf); buf++);
434 		if (!*buf) {
435 			continue;
436 		}
437 		assert(count < symbols); /* file not modified in meanwhile? */
438 		if (sscanf(buffer, "%x %c %32[0-9A-Za-z_.-]s", &address, &symchar, name) != 3) {
439 			fprintf(stderr, "WARNING: syntax error on line %d, skipping.\n", line);
440 			continue;
441 		}
442 		switch (toupper((unsigned char)symchar)) {
443 		case 'T':
444 			symtype = SYMTYPE_TEXT;
445 			offset = offsets[0];
446 			break;
447 		case 'O':	/* AHCC type for _StkSize etc */
448 		case 'D':
449 			symtype = SYMTYPE_DATA;
450 			offset = offsets[1];
451 			break;
452 		case 'B':
453 			symtype = SYMTYPE_BSS;
454 			offset = offsets[2];
455 			break;
456 		default:
457 			fprintf(stderr, "WARNING: unrecognized symbol type '%c' on line %d, skipping.\n", symchar, line);
458 			continue;
459 		}
460 		if (!(gettype & symtype)) {
461 			continue;
462 		}
463 		address += offset;
464 		if (address > maxaddr) {
465 			fprintf(stderr, "WARNING: invalid address 0x%x on line %d, skipping.\n", address, line);
466 			continue;
467 		}
468 		list->names[count].address = address;
469 		list->names[count].type = symtype;
470 		list->names[count].name = strdup(name);
471 		assert(list->names[count].name);
472 		count++;
473 	}
474 	list->symbols = symbols;
475 	list->count = count;
476 	return list;
477 }
478 
479 /**
480  * Return true if given FILE* is Atari program.
481  */
is_atari_program(FILE * fp)482 static bool is_atari_program(FILE *fp)
483 {
484 	long oldpos = ftell(fp);
485 	Uint16 magic;
486 
487 	fseek(fp, 0, SEEK_SET);
488 	if (fread(&magic, sizeof(magic), 1, fp) != 1) {
489 		return false;
490 	}
491 	fseek(fp, oldpos, SEEK_SET);
492 
493 	return (SDL_SwapBE16(magic) == 0x601A);
494 }
495 
496 /**
497  * Load symbols of given type and the symbol address addresses from
498  * the given file and add given offsets to the addresses.
499  * Return symbols list or NULL for failure.
500  */
Symbols_Load(const char * filename,Uint32 * offsets,Uint32 maxaddr)501 static symbol_list_t* Symbols_Load(const char *filename, Uint32 *offsets, Uint32 maxaddr)
502 {
503 	symbol_list_t *list;
504 	FILE *fp;
505 
506 	if (!(fp = fopen(filename, "r"))) {
507 		fprintf(stderr, "ERROR: opening '%s' failed!\n", filename);
508 		return NULL;
509 	}
510 	if (is_atari_program(fp)) {
511 		const char *last = CurrentProgramPath;
512 		if (!last) {
513 			/* "pc=text" breakpoint used as point for loading program symbols gives false hits during bootup */
514 			fprintf(stderr, "WARNING: no program loaded yet (through GEMDOS HD emu)!\n");
515 		} else if (strcmp(last, filename) != 0) {
516 			fprintf(stderr, "WARNING: given program doesn't match last program executed by GEMDOS HD emulation:\n\t%s\n", last);
517 		}
518 		fprintf(stderr, "Reading symbols from program '%s' symbol table...\n", filename);
519 		list = symbols_load_binary(fp, SYMTYPE_ALL);
520 		SymbolsAreForProgram = true;
521 	} else {
522 		fprintf(stderr, "Reading 'nm' style ASCII symbols from '%s'...\n", filename);
523 		list = symbols_load_ascii(fp, offsets, maxaddr, SYMTYPE_ALL);
524 		SymbolsAreForProgram = false;
525 	}
526 	fclose(fp);
527 
528 	if (!list) {
529 		fprintf(stderr, "ERROR: reading symbols from '%s' failed!\n", filename);
530 		return NULL;
531 	}
532 
533 	if (list->count < list->symbols) {
534 		if (!list->count) {
535 			fprintf(stderr, "ERROR: no valid symbols in '%s', loading failed!\n", filename);
536 			symbol_list_free(list);
537 			return NULL;
538 		}
539 		/* parsed less than there were "content" lines */
540 		list->names = realloc(list->names, list->count * sizeof(symbol_t));
541 		assert(list->names);
542 	}
543 
544 	/* copy name list to address list */
545 	list->addresses = malloc(list->count * sizeof(symbol_t));
546 	assert(list->addresses);
547 	memcpy(list->addresses, list->names, list->count * sizeof(symbol_t));
548 
549 	/* sort both lists, with different criteria */
550 	qsort(list->addresses, list->count, sizeof(symbol_t), symbols_by_address);
551 	qsort(list->names, list->count, sizeof(symbol_t), symbols_by_name);
552 
553 	fprintf(stderr, "Loaded %d symbols from '%s'.\n", list->count, filename);
554 	return list;
555 }
556 
557 
558 /**
559  * Free read symbols.
560  */
Symbols_Free(symbol_list_t * list)561 static void Symbols_Free(symbol_list_t* list)
562 {
563 	int i;
564 
565 	if (!list) {
566 		return;
567 	}
568 	assert(list->count);
569 	for (i = 0; i < list->count; i++) {
570 		free(list->names[i].name);
571 	}
572 	free(list->addresses);
573 	free(list->names);
574 
575 	/* catch use of freed list */
576 	list->addresses = NULL;
577 	list->names = NULL;
578 	list->count = 0;
579 	free(list);
580 }
581 
582 
583 /* ---------------- symbol name completion support ------------------ */
584 
585 /**
586  * Helper for symbol name completion and finding their addresses.
587  * STATE = 0 -> different text from previous one.
588  * Return (copy of) next name or NULL if no matches.
589  */
Symbols_MatchByName(symbol_list_t * list,symtype_t symtype,const char * text,int state)590 static char* Symbols_MatchByName(symbol_list_t* list, symtype_t symtype, const char *text, int state)
591 {
592 	static int i, len;
593 	const symbol_t *entry;
594 
595 	if (!list) {
596 		return NULL;
597 	}
598 
599 	if (!state) {
600 		/* first match */
601 		len = strlen(text);
602 		i = 0;
603 	}
604 
605 	/* next match */
606 	entry = list->names;
607 	while (i < list->count) {
608 		if ((entry[i].type & symtype) &&
609 		    strncmp(entry[i].name, text, len) == 0) {
610 			return strdup(entry[i++].name);
611 		} else {
612 			i++;
613 		}
614 	}
615 	return NULL;
616 }
617 
618 /**
619  * Readline match callbacks for CPU symbol name completion.
620  * STATE = 0 -> different text from previous one.
621  * Return next match or NULL if no matches.
622  */
Symbols_MatchCpuAddress(const char * text,int state)623 char* Symbols_MatchCpuAddress(const char *text, int state)
624 {
625 	return Symbols_MatchByName(CpuSymbolsList, SYMTYPE_ALL, text, state);
626 }
Symbols_MatchCpuCodeAddress(const char * text,int state)627 char* Symbols_MatchCpuCodeAddress(const char *text, int state)
628 {
629 	return Symbols_MatchByName(CpuSymbolsList, SYMTYPE_TEXT, text, state);
630 }
Symbols_MatchCpuDataAddress(const char * text,int state)631 char* Symbols_MatchCpuDataAddress(const char *text, int state)
632 {
633 	return Symbols_MatchByName(CpuSymbolsList, SYMTYPE_DATA|SYMTYPE_BSS, text, state);
634 }
635 
636 /**
637  * Readline match callback for DSP symbol name completion.
638  * STATE = 0 -> different text from previous one.
639  * Return next match or NULL if no matches.
640  */
Symbols_MatchDspAddress(const char * text,int state)641 char* Symbols_MatchDspAddress(const char *text, int state)
642 {
643 	return Symbols_MatchByName(DspSymbolsList, SYMTYPE_ALL, text, state);
644 }
Symbols_MatchDspCodeAddress(const char * text,int state)645 char* Symbols_MatchDspCodeAddress(const char *text, int state)
646 {
647 	return Symbols_MatchByName(DspSymbolsList, SYMTYPE_TEXT, text, state);
648 }
Symbols_MatchDspDataAddress(const char * text,int state)649 char* Symbols_MatchDspDataAddress(const char *text, int state)
650 {
651 	return Symbols_MatchByName(DspSymbolsList, SYMTYPE_DATA|SYMTYPE_BSS, text, state);
652 }
653 
654 
655 /* ---------------- symbol name -> address search ------------------ */
656 
657 /**
658  * Search symbol of given type by name.
659  * Return symbol if name matches, zero otherwise.
660  */
Symbols_SearchByName(symbol_list_t * list,symtype_t symtype,const char * name)661 static const symbol_t* Symbols_SearchByName(symbol_list_t* list, symtype_t symtype, const char *name)
662 {
663 	symbol_t *entries;
664 	/* left, right, middle */
665         int l, r, m, dir;
666 
667 	if (!list) {
668 		return NULL;
669 	}
670 	entries = list->names;
671 
672 	/* bisect */
673 	l = 0;
674 	r = list->count - 1;
675 	do {
676 		m = (l+r) >> 1;
677 		dir = strcmp(entries[m].name, name);
678 		if (dir == 0 && (entries[m].type & symtype)) {
679 			return &(entries[m]);
680 		}
681 		if (dir > 0) {
682 			r = m-1;
683 		} else {
684 			l = m+1;
685 		}
686 	} while (l <= r);
687 	return NULL;
688 }
689 
690 /**
691  * Set given CPU symbol's address to variable and return TRUE if one was found.
692  */
Symbols_GetCpuAddress(symtype_t symtype,const char * name,Uint32 * addr)693 bool Symbols_GetCpuAddress(symtype_t symtype, const char *name, Uint32 *addr)
694 {
695 	const symbol_t *entry;
696 	entry = Symbols_SearchByName(CpuSymbolsList, symtype, name);
697 	if (entry) {
698 		*addr = entry->address;
699 		return true;
700 	}
701 	return false;
702 }
703 
704 /**
705  * Set given DSP symbol's address to variable and return TRUE if one was found.
706  */
Symbols_GetDspAddress(symtype_t symtype,const char * name,Uint32 * addr)707 bool Symbols_GetDspAddress(symtype_t symtype, const char *name, Uint32 *addr)
708 {
709 	const symbol_t *entry;
710 	entry = Symbols_SearchByName(DspSymbolsList, symtype, name);
711 	if (entry) {
712 		*addr = entry->address;
713 		return true;
714 	}
715 	return false;
716 }
717 
718 
719 /* ---------------- symbol address -> name search ------------------ */
720 
721 /**
722  * Search symbol by address.
723  * Return symbol index if address matches, -1 otherwise.
724  */
Symbols_SearchByAddress(symbol_list_t * list,Uint32 addr)725 static int Symbols_SearchByAddress(symbol_list_t* list, Uint32 addr)
726 {
727 	symbol_t *entries;
728 	/* left, right, middle */
729         int l, r, m;
730 	Uint32 curr;
731 
732 	if (!list) {
733 		return -1;
734 	}
735 	entries = list->addresses;
736 
737 	/* bisect */
738 	l = 0;
739 	r = list->count - 1;
740 	do {
741 		m = (l+r) >> 1;
742 		curr = entries[m].address;
743 		if (curr == addr) {
744 			return m;
745 		}
746 		if (curr > addr) {
747 			r = m-1;
748 		} else {
749 			l = m+1;
750 		}
751 	} while (l <= r);
752 	return -1;
753 }
754 
755 /**
756  * Search CPU symbol by address.
757  * Return symbol name if address matches, NULL otherwise.
758  * Returned name is valid only until next Symbols_* function call.
759  */
Symbols_GetByCpuAddress(Uint32 addr)760 const char* Symbols_GetByCpuAddress(Uint32 addr)
761 {
762 	int idx = Symbols_SearchByAddress(CpuSymbolsList, addr);
763 	if (idx < 0) {
764 		return NULL;
765 	}
766 	return CpuSymbolsList->addresses[idx].name;
767 }
768 /**
769  * Search DSP symbol by address.
770  * Return symbol name if address matches, NULL otherwise.
771  * Returned name is valid only until next Symbols_* function call.
772  */
Symbols_GetByDspAddress(Uint32 addr)773 const char* Symbols_GetByDspAddress(Uint32 addr)
774 {
775 	int idx = Symbols_SearchByAddress(DspSymbolsList, addr);
776 	if (idx < 0) {
777 		return NULL;
778 	}
779 	return DspSymbolsList->addresses[idx].name;
780 }
781 
782 /**
783  * Search CPU symbol by address.
784  * Return symbol index if address matches, -1 otherwise.
785  */
Symbols_GetCpuAddressIndex(Uint32 addr)786 int Symbols_GetCpuAddressIndex(Uint32 addr)
787 {
788 	return Symbols_SearchByAddress(CpuSymbolsList, addr);
789 }
790 
791 /**
792  * Search DSP symbol by address.
793  * Return symbol index if address matches, -1 otherwise.
794  */
Symbols_GetDspAddressIndex(Uint32 addr)795 int Symbols_GetDspAddressIndex(Uint32 addr)
796 {
797 	return Symbols_SearchByAddress(DspSymbolsList, addr);
798 }
799 
800 /**
801  * Return how many symbols are loaded/available
802  */
Symbols_CpuCount(void)803 int Symbols_CpuCount(void)
804 {
805 	return (CpuSymbolsList ? CpuSymbolsList->count : 0);
806 }
Symbols_DspCount(void)807 int Symbols_DspCount(void)
808 {
809 	return (DspSymbolsList ? DspSymbolsList->count : 0);
810 }
811 
812 /* ---------------- symbol showing ------------------ */
813 
814 /**
815  * Show symbols from given list with paging.
816  */
Symbols_Show(symbol_list_t * list,const char * sorttype)817 static void Symbols_Show(symbol_list_t* list, const char *sorttype)
818 {
819 	symbol_t *entry, *entries;
820 	char symchar;
821 	int i;
822 
823 	if (!list) {
824 		fprintf(stderr, "No symbols!\n");
825 		return;
826 	}
827 
828 	if (strcmp("addr", sorttype) == 0) {
829 		entries = list->addresses;
830 	} else {
831 		entries = list->names;
832 	}
833 	fprintf(stderr, "%s symbols sorted by %s:\n",
834 		(list == CpuSymbolsList ? "CPU" : "DSP"), sorttype);
835 
836 	for (entry = entries, i = 0; i < list->count; i++, entry++) {
837 		symchar = symbol_char(entry->type);
838 		fprintf(stderr, "0x%08x %c %s\n",
839 			entry->address, symchar, entry->name);
840 		if (i && i % 20 == 0) {
841 			fprintf(stderr, "--- q to exit listing, just enter to continue --- ");
842 			if (toupper(getchar()) == 'Q') {
843 				return;
844 			}
845 		}
846 	}
847 }
848 
849 /* ---------------- binary load handling ------------------ */
850 
851 
852 /**
853  * Remove last opened program path.
854  */
Symbols_RemoveCurrentProgram(void)855 void Symbols_RemoveCurrentProgram(void)
856 {
857 	if (CurrentProgramPath) {
858 		free(CurrentProgramPath);
859 		CurrentProgramPath = NULL;
860 
861 		if (SymbolsAreForProgram) {
862 			Symbols_Free(CpuSymbolsList);
863 			CpuSymbolsList = NULL;
864 		}
865 	}
866 	AutoLoadFailed = false;
867 }
868 
869 /**
870  * Set last opened program path.
871  */
Symbols_ChangeCurrentProgram(FILE * fp,const char * path)872 void Symbols_ChangeCurrentProgram(FILE *fp, const char *path)
873 {
874 	if (is_atari_program(fp)) {
875 		Symbols_RemoveCurrentProgram();
876 		CurrentProgramPath = strdup(path);
877 	}
878 }
879 
880 /**
881  * Load symbols for last opened program.
882  */
Symbols_LoadCurrentProgram(void)883 void Symbols_LoadCurrentProgram(void)
884 {
885 	/* symbols already loaded, program path missing or previous load failed? */
886 	if (CpuSymbolsList || !CurrentProgramPath || AutoLoadFailed) {
887 		return;
888 	}
889 	CpuSymbolsList = Symbols_Load(CurrentProgramPath, NULL, 0);
890 	if (!CpuSymbolsList) {
891 		AutoLoadFailed = true;
892 	} else {
893 		AutoLoadFailed = false;
894 	}
895 }
896 
897 /* ---------------- command parsing ------------------ */
898 
899 /**
900  * Readline match callback to list symbols subcommands.
901  * STATE = 0 -> different text from previous one.
902  * Return next match or NULL if no matches.
903  */
Symbols_MatchCommand(const char * text,int state)904 char *Symbols_MatchCommand(const char *text, int state)
905 {
906 	static const char* subs[] = {
907 		"addr", "free", "name", "prg"
908 	};
909 	return DebugUI_MatchHelper(subs, ARRAYSIZE(subs), text, state);
910 }
911 
912 const char Symbols_Description[] =
913 	"<filename|prg|addr|name|free> [<T offset> [<D offset> <B offset>]]\n"
914 	"\tLoads symbol names and their addresses from the given file.\n"
915 	"\tIf there were previously loaded symbols, they're replaced.\n"
916 	"\n"
917 	"\tGiving 'prg' instead of a file name, loads DRI/GST symbol table\n"
918 	"\tfrom the last program executed through the GEMDOS HD emulation.\n"
919 	"\n"
920 	"\tGiving either 'name' or 'addr' instead of a file name, will\n"
921 	"\tlist the currently loaded symbols.  Giving 'free' will remove\n"
922 	"\tthe loaded symbols.\n"
923 	"\n"
924 	"\tIf one base address/offset is given, its added to all addresses.\n"
925 	"\tIf three offsets are given (and non-zero), they're applied to\n"
926 	"\ttext (T), data (D) and BSS (B) symbols.  Given offsets are used\n"
927 	"\tonly when loading ASCII symbol files.";
928 
929 /**
930  * Handle debugger 'symbols' command and its arguments
931  */
Symbols_Command(int nArgc,char * psArgs[])932 int Symbols_Command(int nArgc, char *psArgs[])
933 {
934 	enum { TYPE_NONE, TYPE_CPU, TYPE_DSP } listtype;
935 	Uint32 offsets[3], maxaddr;
936 	symbol_list_t *list;
937 	const char *file;
938 	int i;
939 
940 	if (strcmp("dspsymbols", psArgs[0]) == 0) {
941 		listtype = TYPE_DSP;
942 		maxaddr = 0xFFFF;
943 	} else if (strcmp("symbols", psArgs[0]) == 0) {
944 		listtype = TYPE_CPU;
945 		maxaddr = 0xFFFFFF;
946 	} else {
947 		listtype = TYPE_NONE;
948 		maxaddr = 0;
949 	}
950 	if (nArgc < 2 || listtype == TYPE_NONE) {
951 		return DebugUI_PrintCmdHelp(psArgs[0]);
952 	}
953 	file = psArgs[1];
954 
955 	/* handle special cases */
956 	if (strcmp(file, "name") == 0 || strcmp(file, "addr") == 0) {
957 		list = (listtype == TYPE_DSP ? DspSymbolsList : CpuSymbolsList);
958 		Symbols_Show(list, file);
959 		return DEBUGGER_CMDDONE;
960 	}
961 	if (strcmp(file, "free") == 0) {
962 		if (listtype == TYPE_DSP) {
963 			Symbols_Free(DspSymbolsList);
964 			DspSymbolsList = NULL;
965 		} else {
966 			Symbols_Free(CpuSymbolsList);
967 			CpuSymbolsList = NULL;
968 		}
969 		return DEBUGGER_CMDDONE;
970 	}
971 
972 	/* get offsets */
973 	offsets[0] = 0;
974 	for (i = 0; i < ARRAYSIZE(offsets); i++) {
975 		if (i+2 < nArgc) {
976 			int dummy;
977 			Eval_Expression(psArgs[i+2], &(offsets[i]), &dummy, listtype==TYPE_DSP);
978 		} else {
979 			/* default to first (text) offset */
980 			offsets[i] = offsets[0];
981 		}
982 	}
983 
984 	if (strcmp(file, "prg") == 0) {
985 		file = CurrentProgramPath;
986 		if (!file) {
987 			fprintf(stderr, "ERROR: no program loaded (through GEMDOS HD emu)!\n");
988 			return DEBUGGER_CMDDONE;
989 		}
990 	}
991 	list = Symbols_Load(file, offsets, maxaddr);
992 	if (list) {
993 		if (listtype == TYPE_CPU) {
994 			Symbols_Free(CpuSymbolsList);
995 			CpuSymbolsList = list;
996 		} else {
997 			Symbols_Free(DspSymbolsList);
998 			DspSymbolsList = list;
999 		}
1000 	} else {
1001 		DebugUI_PrintCmdHelp(psArgs[0]);
1002 	}
1003 	return DEBUGGER_CMDDONE;
1004 }
1005