1 /*
2 * UAE - The Un*x Amiga Emulator
3 *
4 * Segment Tracker
5 *
6 * Written by Christian Vogelgsang <chris@vogelgsang.org>
7 */
8
9 #include "sysconfig.h"
10 #include "sysdeps.h"
11 #include "options.h"
12
13 #include "uae/memory.h"
14 #include "autoconf.h"
15 #include "traps.h"
16 #include "uae/patch.h"
17 #include "newcpu.h"
18 #include "uae/debuginfo.h"
19 #include "uae/segtracker.h"
20
21 static uae_u32 init_addr;
22 static uae_u32 dos_base;
23
24 static patch_func pf_OpenLibrary;
25 static patch_func pf_LoadSeg;
26 static patch_func pf_UnLoadSeg;
27 static patch_func pf_NewLoadSeg;
28
29 #ifdef _WIN32
30 #define strcasestr strstr
31 #warning Needs strcasestr replacement
32 #endif
33
34 int segtracker_enabled = 0;
35
36 /* patch the interesting DOS functions.
37 Since we call exec's SetFunction to perform the patch it will work on
38 V36 and pre too as there the patch is not performed. */
patch_dos(TrapContext * ctx)39 static void patch_dos(TrapContext *ctx)
40 {
41 uae_u16 ver = get_word(dos_base + 20);
42 write_log("segtracker: dos V%d -> %s\n", ver, (ver <= 36) ? "can't patch":"patched");
43
44 patch_func_set(ctx, dos_base, -150, &pf_LoadSeg);
45 patch_func_set(ctx, dos_base, -156, &pf_UnLoadSeg);
46 patch_func_set(ctx, dos_base, -768, &pf_NewLoadSeg);
47 }
48
49 /* patched EXEC: OpenLibrary call */
OpenLibrary(TrapContext * ctx)50 static uae_u32 REGPARAM2 OpenLibrary(TrapContext *ctx)
51 {
52 uaecptr name_addr = m68k_areg (regs, 1); // a1 = lib name
53 uaecptr lib_base = m68k_dreg( regs, 0 ); // d0 = lib base
54 uae_u8 *name = get_real_address(name_addr);
55
56 /* here comes a hack to patch dos.library calls:
57 wait for the first one opening dos.library and then
58 patch my functions... */
59 if(dos_base == 0) {
60 if(strcmp((const char *)name, "dos.library")==0) {
61 dos_base = lib_base;
62 patch_dos(ctx);
63 }
64 }
65
66 return 0;
67 }
68
69 /* global list of all seglists */
70 seglist_pool segtracker_pool;
71
72 /* dump the segment list */
segtracker_dump(const char * match)73 void segtracker_dump(const char *match)
74 {
75 int sl_num = 0;
76 seglist *sl = segtracker_pool.first;
77 while(sl != NULL) {
78 if( (match == NULL) || (strcasestr(sl->name, match)!=NULL) ) {
79 printf("'%s' @%08x\n", sl->name, sl->addr);
80 segment *s = sl->segments;
81 int num = 0;
82 while(s->addr != 0) {
83 printf(" #%02d [%08x,%08x,%08x]", num,
84 s->addr, s->size, s->addr + s->size);
85
86 /* show attached debug info */
87 debug_segment *ds = s->debug;
88 if(ds != NULL) {
89 printf(" %3d symbols, %3d src files\n",
90 ds->num_symbols, ds->num_src_files);
91
92 /* dump symbols */
93 if(match != NULL) {
94 debug_symbol *symbol = ds->symbols;
95 for(int i=0;i<ds->num_symbols;i++) {
96 uae_u32 addr = s->addr + symbol->offset;
97 printf(" %08x %s\n", addr, symbol->name);
98 symbol++;
99 }
100 }
101 } else {
102 printf("\n");
103 }
104
105 s++;
106 num++;
107 }
108 sl_num++;
109 }
110 sl = sl->next;
111 }
112 printf("found %d seglists.\n", sl_num);
113 }
114
115 /* search a segment by address */
segtracker_search_address(uae_u32 addr,seglist ** found_sl,int * num_seg)116 int segtracker_search_address(uae_u32 addr, seglist **found_sl, int *num_seg)
117 {
118 seglist *sl = segtracker_pool.first;
119 while(sl != NULL) {
120 int num = 0;
121 segment *s = sl->segments;
122 while(s->addr != 0) {
123
124 uae_u32 end = s->addr + s->size;
125 if((addr >= s->addr) && (addr < end)) {
126 *found_sl = sl;
127 *num_seg = num;
128 return 1;
129 }
130
131 s++;
132 num++;
133 }
134 sl = sl->next;
135 }
136 return 0;
137 }
138
add_seglist(const char * name,uae_u32 seglist_addr)139 static void add_seglist(const char *name, uae_u32 seglist_addr)
140 {
141 if(seglist_addr == 0) {
142 return;
143 }
144
145 /* first count the number of segments */
146 int num_segs = 0;
147 uae_u32 ptr = seglist_addr;
148 while(ptr != 0) {
149 ptr = get_long(ptr) << 2;
150 num_segs ++;
151 }
152
153 /* alloc seglist */
154 seglist *sl = xmalloc(seglist, 1);
155 if(sl == NULL) {
156 write_log("segtracker: NO seglist MEMORY!\n");
157 return;
158 }
159 segment *segs = xmalloc(segment, num_segs + 1); // add an empty segment
160 if(segs == NULL) {
161 write_log("segtracker: NO segments MEMORY!\n");
162 return;
163 }
164
165 /* fill seglist */
166 sl->name = my_strdup(name);
167 sl->addr = seglist_addr;
168 sl->num_segments = num_segs;
169 sl->segments = segs;
170 sl->next = NULL;
171 sl->prev = NULL;
172 sl->debug = NULL;
173
174 /* fill segments */
175 ptr = seglist_addr;
176 segment *s = segs;
177 while(ptr != 0) {
178 s->size = get_long(ptr - 4); // size of BPTR + segment
179 s->size -= 8; // correct size
180 s->addr = ptr + 4;
181 s->debug = NULL;
182 ptr = get_long(ptr) << 2; // BPTR to next segment
183 s++;
184 }
185 /* last segment is zero */
186 s->size = 0;
187 s->addr = 0;
188
189 /* link seglist */
190 if(segtracker_pool.first == NULL) {
191 segtracker_pool.first = sl;
192 segtracker_pool.last = sl;
193 } else {
194 sl->prev = segtracker_pool.last;
195 segtracker_pool.last->next = sl;
196 segtracker_pool.last = sl;
197 }
198 segtracker_pool.num_seglists ++;
199 }
200
del_seglist(seglist * sl)201 static void del_seglist(seglist *sl)
202 {
203 if(sl->next != NULL) {
204 sl->next->prev = sl->prev;
205 }
206 if(sl->prev != NULL) {
207 sl->prev->next = sl->next;
208 }
209 if(sl == segtracker_pool.first) {
210 segtracker_pool.first = sl->next;
211 }
212 if(sl == segtracker_pool.last) {
213 segtracker_pool.last = sl->prev;
214 }
215 segtracker_pool.num_seglists --;
216
217 if(sl->name != NULL) {
218 xfree(sl->name);
219 }
220 if(sl->segments != NULL) {
221 xfree(sl->segments);
222 }
223
224 if(sl->debug != NULL) {
225 debug_info_free_file(sl->debug);
226 }
227
228 xfree(sl);
229 }
230
del_all_seglists(void)231 static void del_all_seglists(void)
232 {
233 seglist *sl = segtracker_pool.first;
234 while(sl != NULL) {
235 seglist *next_sl = sl->next;
236 del_seglist(sl);
237 sl = next_sl;
238 }
239 segtracker_pool.num_seglists = 0;
240 }
241
rem_seglist(uae_u32 seglist_addr)242 static void rem_seglist(uae_u32 seglist_addr)
243 {
244 /* similar to original SegTracker support partial segment unloading */
245 uae_u32 ptr = seglist_addr;
246 while(ptr != 0) {
247 /* find segment in seglist */
248 seglist *sl = segtracker_pool.first;
249 int found = 0;
250 while(sl != NULL) {
251 /* check all segments */
252 segment *s = sl->segments;
253 int any_left = 0;
254 while(s->addr != 0) {
255 /* found segment -> set its size to 0 */
256 if(s->addr == (ptr+4)) {
257 s->size = 0;
258 found = 1;
259 }
260 if(s->size != 0) {
261 any_left ++;
262 }
263 s++;
264 }
265
266 /* delete seglist */
267 seglist *sl_next = sl->next;
268 if(any_left == 0) {
269 del_seglist(sl);
270 }
271
272 if(found) {
273 break;
274 }
275
276 sl = sl_next;
277 }
278
279 /* segment not found?! */
280 if(!found) {
281 write_log("segtracker: segment %08x NOT found!\n", ptr);
282 }
283
284 /* next segment of input seglist */
285 ptr = get_long(ptr) << 2;
286 }
287 }
288
289 /* remove all tracked segment lists */
segtracker_clear(void)290 void segtracker_clear(void)
291 {
292 del_all_seglists();
293 }
294
295 /* patched DOS: LoadSeg call */
LoadSeg(TrapContext * ctx)296 static uae_u32 REGPARAM2 LoadSeg(TrapContext *ctx)
297 {
298 uaecptr name_addr = m68k_areg(regs,0); // a0 = name (was d1)
299 uaecptr seglist_baddr = m68k_dreg(regs,0); // d0 = seglist
300 uaecptr seglist_addr = seglist_baddr << 2;
301
302 const char *name = (char *)get_real_address(name_addr);
303 //printf("LoadSeg(%s) -> %08x\n",name, seglist_addr);
304 add_seglist(name, seglist_addr);
305
306 return 0;
307 }
308
309 /* patched DOS: NewLoadSeg call */
NewLoadSeg(TrapContext * ctx)310 static uae_u32 REGPARAM2 NewLoadSeg(TrapContext *ctx)
311 {
312 uaecptr name_addr = m68k_areg(regs,0); // a0 = name (was d1)
313 uaecptr seglist_baddr = m68k_dreg(regs,0); // d0 = seglist
314 uaecptr seglist_addr = seglist_baddr << 2;
315
316 const char *name = (char *)get_real_address(name_addr);
317 //printf("NewLoadSeg(%s) -> %08x\n",name, seglist_addr);
318 add_seglist(name, seglist_addr);
319
320 return 0;
321 }
322
323 /* patched DOS: UnLoadSeg call */
UnLoadSeg(TrapContext * ctx)324 static uae_u32 REGPARAM2 UnLoadSeg(TrapContext *ctx)
325 {
326 uaecptr seglist_baddr = m68k_dreg(regs,1); // d1 = seglist
327 uaecptr seglist_addr = seglist_baddr << 2;
328
329 //printf("UnLoadSeg(%08x)\n", seglist_addr);
330 rem_seglist(seglist_addr);
331
332 return 0;
333 }
334
335 /* When the dummy resident is executed then call this function via trap.
336 Our first patch is to track OpenLibrary calls so we can spot the
337 dos.library */
segtracker_init(TrapContext * ctx)338 static uae_u32 REGPARAM2 segtracker_init(TrapContext *ctx)
339 {
340 /* patch exec funcs */
341 uae_u32 exec_base = get_long(4);
342 patch_func_set(ctx, exec_base, -552, &pf_OpenLibrary);
343 write_log("segtracker: patched OpenLibrary\n");
344
345 return 0;
346 }
347
348 /* main entry of SegmentTracker for each Amiga run: setting up AutoConf area...
349 if tracker is enabled then install a dummy resident that does the
350 function patching in its init function */
segtracker_startup(uaecptr resaddr)351 uaecptr segtracker_startup (uaecptr resaddr)
352 {
353 /* reset dos patching and clear seglist */
354 dos_base = 0;
355 del_all_seglists();
356
357 if(!segtracker_enabled) {
358 return resaddr;
359 }
360
361 write_log("segtracker: startup\n");
362
363 /* setup a fake resident resident */
364 put_word (resaddr + 0x0, 0x4AFC);
365 put_long (resaddr + 0x2, resaddr);
366 put_long (resaddr + 0x6, resaddr + 0x1A); /* Continue scan here */
367 put_word (resaddr + 0xA, 0x0101); /* RTF_COLDSTART; Version 1 */
368 put_word (resaddr + 0xC, 0x0078); /* NT_UNKNOWN; pri 05 */
369 put_long (resaddr + 0xE, 0); /* no name */
370 put_long (resaddr + 0x12, 0); /* no id */
371 put_long (resaddr + 0x16, init_addr);
372 resaddr += 0x1A;
373
374 return resaddr;
375 }
376
377 /* initial entry of SegmentTracker: on first startup of UAE register some
378 traps for the patched functions */
segtracker_install(void)379 void segtracker_install (void)
380 {
381 write_log("segtracker: install\n");
382
383 // setup init trap to setup patches
384 init_addr = here ();
385 calltrap (deftrap2 (segtracker_init, TRAPFLAG_EXTRA_STACK, _T("segtracker_init")));
386 dw (RTS);
387
388 // exec
389 patch_func_init_post_call(OpenLibrary, &pf_OpenLibrary,
390 TRAPFLAG_EXTRA_STACK, PATCH_SAVE_NONE);
391 // dos
392 patch_func_init_post_call(LoadSeg, &pf_LoadSeg, 0, PATCH_SAVE_D1_RESTORE_A0);
393 patch_func_init_post_call(NewLoadSeg, &pf_NewLoadSeg, 0, PATCH_SAVE_D1_RESTORE_A0);
394 patch_func_init_pre_call(UnLoadSeg, &pf_UnLoadSeg, 0);
395 }
396
segtracker_find_by_name(const char * name)397 seglist *segtracker_find_by_name(const char *name)
398 {
399 seglist *sl = segtracker_pool.first;
400 while(sl != NULL) {
401 const char *sname = sl->name;
402 if(strcasestr(sname, name)!=NULL) {
403 return sl;
404 }
405 sl = sl->next;
406 }
407 return NULL;
408 }
409
segtracker_add_debug_info(seglist * seglist,debug_file * file,const char ** err)410 int segtracker_add_debug_info(seglist *seglist, debug_file *file, const char **err)
411 {
412 /* already has debug info? */
413 if(seglist->debug != NULL) {
414 if(err != NULL) {
415 *err = "Already has debug info!";
416 }
417 return -3;
418 }
419
420 /* first validate if segments match in both seglist and file */
421 if(seglist->num_segments != file->num_segments) {
422 if(err != NULL) {
423 *err = "Number of segments mismatch!";
424 }
425 return -1;
426 }
427 for(int i=0;i<seglist->num_segments;i++) {
428 segment *s = &seglist->segments[i];
429 debug_segment *ds = &file->segments[i];
430 if(s->size != ds->size) {
431 if(err != NULL) {
432 *err = "Segment size mismatch!";
433 }
434 return -2;
435 }
436 }
437
438 /* seems to be ok -> set infos */
439 seglist->debug = file;
440 for(int i=0;i<seglist->num_segments;i++) {
441 segment *s = &seglist->segments[i];
442 debug_segment *ds = &file->segments[i];
443 s->debug = ds;
444 }
445
446 return 0;
447 }
448
segtracker_find_symbol(const segment * seg,uae_u32 offset,debug_symbol ** ret_symbol,uae_u32 * ret_reloff)449 int segtracker_find_symbol(const segment *seg, uae_u32 offset,
450 debug_symbol **ret_symbol, uae_u32 *ret_reloff)
451 {
452 if(seg->debug != NULL) {
453 return debug_info_find_symbol(seg->debug, offset, ret_symbol, ret_reloff);
454 } else {
455 return -1;
456 }
457 }
458
segtracker_find_src_line(const segment * seg,uae_u32 offset,debug_src_file ** ret_file,debug_src_line ** ret_line,uae_u32 * ret_reloff)459 int segtracker_find_src_line(const segment *seg, uae_u32 offset,
460 debug_src_file **ret_file,
461 debug_src_line **ret_line, uae_u32 *ret_reloff)
462 {
463 if(seg->debug != NULL) {
464 return debug_info_find_src_line(seg->debug, offset, ret_file, ret_line, ret_reloff);
465 } else {
466 return -1;
467 }
468 }
469
segtracker_dump_symbols(const char * name)470 void segtracker_dump_symbols(const char *name)
471 {
472 int showed_seglist = 0;
473 seglist *sl = segtracker_pool.first;
474 while(sl != NULL) {
475 /* has seglist debug info? */
476 if(sl->debug != NULL) {
477 segment *s = sl->segments;
478 /* run through segments */
479 for(int i=0;i<sl->num_segments;i++) {
480 if(s->debug != NULL) {
481 debug_symbol *ds = s->debug->symbols;
482 int showed_segment = 0;
483 for(int j=0;j<s->debug->num_symbols;j++) {
484 /* does symbol name match? */
485 if(strcasestr(ds->name, name)!=NULL) {
486 if(!showed_seglist) {
487 showed_seglist = 1;
488 printf("'%s'\n", sl->name);
489 }
490 if(!showed_segment) {
491 showed_segment = 1;
492 printf(" #%02d\n", i);
493 }
494 uae_u32 addr = ds->offset + s->addr;
495 printf(" %08x: %s\n", addr, ds->name);
496 }
497 ds++;
498 }
499 }
500 s++;
501 }
502 }
503 showed_seglist = 0;
504 sl = sl->next;
505 }
506 }
507
segtracker_dump_src_lines(const char * name,int line)508 void segtracker_dump_src_lines(const char *name, int line)
509 {
510 int showed_seglist = 0;
511 seglist *sl = segtracker_pool.first;
512 while(sl != NULL) {
513 /* has seglist debug info? */
514 if(sl->debug != NULL) {
515 segment *seg = sl->segments;
516 /* run through segments */
517 for(int i=0;i<sl->num_segments;i++) {
518 debug_segment *ds = seg->debug;
519 if(ds != NULL) {
520 int showed_segment = 0;
521
522 /* now look in src files */
523 debug_src_file *sf = ds->src_files;
524 while(sf != NULL) {
525 /* does src_file name match? */
526 if(strcasestr(sf->src_file, name)!=NULL) {
527 /* check line number */
528 debug_src_line *l = sf->lines;
529 for(int j=0;j<sf->num_lines;j++) {
530 /* found line */
531 if(l->line == line) {
532 if(!showed_seglist) {
533 showed_seglist = 1;
534 printf("'%s'\n", sl->name);
535 }
536 if(!showed_segment) {
537 showed_segment = 1;
538 printf(" #%02d\n", i);
539 }
540 uae_u32 addr = l->offset + seg->addr;
541 printf(" %08x: %s:%d\n", addr, sf->src_file, l->line);
542 }
543 l++;
544 }
545 }
546 sf = sf->next;
547 }
548 }
549 seg++;
550 }
551 }
552 showed_seglist = 0;
553 sl = sl->next;
554 }
555 }
556