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