Make a better guess to find the top of the initial stack.
[wine] / debugger / source.c
1 /*
2  * File source.c - source file handling for internal debugger.
3  *
4  * Copyright (C) 1997, Eric Youngdale.
5  *
6  */
7
8 #include "config.h"
9 #include <stdio.h>
10 #include <stdlib.h>
11
12 #include <sys/types.h>
13 #ifdef HAVE_SYS_MMAN_H
14 #include <sys/mman.h>
15 #endif
16 #include <fcntl.h>
17 #include <sys/stat.h>
18 #include <limits.h>
19 #include <string.h>
20 #include <unistd.h>
21 #ifndef PATH_MAX
22 #define PATH_MAX _MAX_PATH
23 #endif
24
25 #include "debugger.h"
26
27 struct searchlist
28 {
29   char * path;
30   struct searchlist * next;
31 };
32
33
34 struct open_filelist
35 {
36   char                  * path;
37   char                  * real_path;
38   struct open_filelist  * next;
39   unsigned int            size;
40   signed int              nlines;
41   unsigned int          * linelist;
42 };
43
44 static struct open_filelist * ofiles;
45
46 static struct searchlist * listhead;
47 static char DEBUG_current_sourcefile[PATH_MAX];
48 static int DEBUG_start_sourceline = -1;
49 static int DEBUG_end_sourceline = -1;
50
51 void
52 DEBUG_ShowDir(void)
53 {
54   struct searchlist * sl;
55
56   DEBUG_Printf(DBG_CHN_MESG,"Search list :\n");
57   for(sl = listhead; sl; sl = sl->next)
58     {
59       DEBUG_Printf(DBG_CHN_MESG, "\t%s\n", sl->path);
60     }
61   DEBUG_Printf(DBG_CHN_MESG, "\n");
62 }
63
64 void
65 DEBUG_AddPath(const char * path)
66 {
67   struct searchlist * sl;
68
69   sl = (struct searchlist *) DBG_alloc(sizeof(struct searchlist));
70   if( sl == NULL )
71     {
72       return;
73     }
74
75   sl->next = listhead;
76   sl->path = DBG_strdup(path);
77   listhead = sl;
78 }
79
80 void
81 DEBUG_NukePath(void)
82 {
83   struct searchlist * sl;
84   struct searchlist * nxt;
85
86   for(sl = listhead; sl; sl = nxt)
87     {
88       nxt = sl->next;
89       DBG_free(sl->path);
90       DBG_free(sl);
91     }
92
93   listhead = NULL;
94 }
95
96 static
97 int
98 DEBUG_DisplaySource(char * sourcefile, int start, int end)
99 {
100   char                        * addr;
101   char                          buffer[1024];
102   int                           fd;
103   int                           i;
104   struct open_filelist        * ol;
105   int                           nlines;
106   char                        * basename = NULL;
107   char                        * pnt;
108   int                           rtn;
109   struct searchlist           * sl;
110   struct stat                   statbuf;
111   int                           status;
112   char                          tmppath[PATH_MAX];
113
114   /*
115    * First see whether we have the file open already.  If so, then
116    * use that, otherwise we have to try and open it.
117    */
118   for(ol = ofiles; ol; ol = ol->next)
119     {
120       if( strcmp(ol->path, sourcefile) == 0 )
121         {
122           break;
123         }
124     }
125
126   if( ol == NULL )
127     {
128       /*
129        * Try again, stripping the path from the opened file.
130        */
131       basename = strrchr(sourcefile, '\\' );
132       if ( !basename )
133           basename = strrchr(sourcefile, '/' );
134       if ( !basename )
135           basename = sourcefile;
136       else
137           basename++;
138
139       for(ol = ofiles; ol; ol = ol->next)
140         {
141           if( strcmp(ol->path, basename) == 0 )
142             {
143               break;
144             }
145         }
146       
147     }
148
149   if( ol == NULL )
150     {
151       /*
152        * Crapola.  We need to try and open the file.
153        */
154       status = stat(sourcefile, &statbuf);
155       if( status != -1 )
156         {
157           strcpy(tmppath, sourcefile);
158         }
159       else if( (status = stat(basename, &statbuf)) != -1 )
160         {
161           strcpy(tmppath, basename);
162         }
163       else
164         {
165           for(sl = listhead; sl; sl = sl->next)
166             {
167               strcpy(tmppath, sl->path);
168               if( tmppath[strlen(tmppath)-1] != '/' )
169                 {
170                   strcat(tmppath, "/");
171                 }
172               /*
173                * Now append the base file name.
174                */
175               strcat(tmppath, basename);
176               
177               status = stat(tmppath, &statbuf);
178               if( status != -1 )
179                 {
180                   break;
181                 }
182             }
183           
184           if( sl == NULL )
185             {
186               char      zbuf[256];
187               /*
188                * Still couldn't find it.  Ask user for path to add.
189                */
190               sprintf(zbuf, "Enter path to file %s: ", sourcefile);
191               lstrcpynA(tmppath, readline(zbuf), sizeof(tmppath));
192               
193               if( tmppath[strlen(tmppath)-1] == '\n' )
194                 {
195                   tmppath[strlen(tmppath)-1] = '\0';
196                 }
197               
198               if( tmppath[strlen(tmppath)-1] != '/' )
199                 {
200                   strcat(tmppath, "/");
201                 }
202               /*
203                * Now append the base file name.
204                */
205               strcat(tmppath, basename);
206               
207               status = stat(tmppath, &statbuf);
208               if( status == -1 )
209                 {
210                   /*
211                    * OK, I guess the user doesn't really want to see it
212                    * after all.
213                    */
214                   ol = (struct open_filelist *) DBG_alloc(sizeof(*ol));
215                   ol->path = DBG_strdup(sourcefile);
216                   ol->real_path = NULL;
217                   ol->next = ofiles;
218                   ol->nlines = 0;
219                   ol->linelist = NULL;
220                   ofiles = ol;
221                   DEBUG_Printf(DBG_CHN_MESG,"Unable to open file %s\n", tmppath);
222                   return FALSE;
223                 }
224             }
225         }
226       /*
227        * Create header for file.
228        */
229       ol = (struct open_filelist *) DBG_alloc(sizeof(*ol));
230       ol->path = DBG_strdup(sourcefile);
231       ol->real_path = DBG_strdup(tmppath);
232       ol->next = ofiles;
233       ol->nlines = 0;
234       ol->linelist = NULL;
235       ol->size = statbuf.st_size;
236       ofiles = ol;
237
238       /*
239        * Now open and map the file.
240        */
241       fd = open(tmppath, O_RDONLY);
242       if( fd == -1 )
243         {
244           return FALSE;
245         }
246
247       addr = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
248       if( addr == (char *) -1 )
249         {
250           return FALSE;
251         }
252
253       /*
254        * Now build up the line number mapping table.
255        */
256       ol->nlines = 1;
257       pnt = addr;
258       while(pnt < addr + ol->size )
259         {
260           if( *pnt++ == '\n' )
261             {
262               ol->nlines++;
263             }
264         }
265
266       ol->nlines++;
267       ol->linelist = (unsigned int*) DBG_alloc( ol->nlines * sizeof(unsigned int) );
268
269       nlines = 0;
270       pnt = addr;
271       ol->linelist[nlines++] = 0;
272       while(pnt < addr + ol->size )
273         {
274           if( *pnt++ == '\n' )
275             {
276               ol->linelist[nlines++] = pnt - addr;
277             }
278         }
279       ol->linelist[nlines++] = pnt - addr;
280
281     }
282   else
283     {
284       /*
285        * We know what the file is, we just need to reopen it and remap it.
286        */
287       fd = open(ol->real_path, O_RDONLY);
288       if( fd == -1 )
289         {
290           return FALSE;
291         }
292       
293       addr = mmap(0, ol->size, PROT_READ, MAP_PRIVATE, fd, 0);
294       if( addr == (char *) -1 )
295         {
296           return FALSE;
297         }
298     }
299   
300   /*
301    * All we need to do is to display the source lines here.
302    */
303   rtn = FALSE;
304   for(i=start - 1; i <= end - 1; i++)
305     {
306       if( i < 0 || i >= ol->nlines - 1)
307         {
308           continue;
309         }
310
311       rtn = TRUE;
312       memset(&buffer, 0, sizeof(buffer));
313       if( ol->linelist[i+1] != ol->linelist[i] )
314         {
315           memcpy(&buffer, addr + ol->linelist[i], 
316                  (ol->linelist[i+1] - ol->linelist[i]) - 1);
317         }
318       DEBUG_Printf(DBG_CHN_MESG,"%d\t%s\n", i + 1,  buffer);
319     }
320
321   munmap(addr, ol->size);
322   close(fd);
323
324   return rtn;
325
326 }
327
328 void
329 DEBUG_List(struct list_id * source1, struct list_id * source2,
330                          int delta)
331 {
332   int    end;
333   int    rtn;
334   int    start;
335   char * sourcefile;
336
337   /*
338    * We need to see what source file we need.  Hopefully we only have
339    * one specified, otherwise we might as well punt.
340    */
341   if( source1 != NULL 
342       && source2 != NULL 
343       && source1->sourcefile != NULL
344       && source2->sourcefile != NULL 
345       && strcmp(source1->sourcefile, source2->sourcefile) != 0 )
346     {
347       DEBUG_Printf(DBG_CHN_MESG, "Ambiguous source file specification.\n");
348       return;
349     }
350
351   sourcefile = NULL;
352   if( source1 != NULL && source1->sourcefile != NULL )
353     {
354       sourcefile = source1->sourcefile;
355     }
356
357   if( sourcefile == NULL 
358       && source2 != NULL 
359       && source2->sourcefile != NULL )
360     {
361       sourcefile = source2->sourcefile;
362     }
363
364   if( sourcefile == NULL )
365     {
366       sourcefile = (char *) &DEBUG_current_sourcefile;
367     }
368
369   if( sourcefile == NULL )
370     {
371       DEBUG_Printf(DBG_CHN_MESG, "No source file specified.\n");
372       return;
373     }
374
375   /*
376    * Now figure out the line number range to be listed.
377    */
378   start = -1;
379   end = -1;
380
381   if( source1 != NULL )
382     {
383       start = source1->line;
384     }
385
386   if( source2 != NULL )
387     {
388       end = source2->line;
389     }
390
391   if( start == -1 && end == -1 )
392     {
393       if( delta < 0 )
394         {
395           end = DEBUG_start_sourceline;
396           start = end + delta;
397         }
398       else
399         {
400           start = DEBUG_end_sourceline;
401           end = start + delta;
402         }
403     }
404   else if( start == -1 )
405     {
406       start = end + delta;
407     }
408   else if (end == -1)
409     {
410       end = start + delta;
411     }
412
413   /*
414    * Now call this function to do the dirty work.
415    */
416   rtn = DEBUG_DisplaySource(sourcefile, start, end);
417
418   if( sourcefile != (char *) &DEBUG_current_sourcefile )
419     {
420       strcpy(DEBUG_current_sourcefile, sourcefile);
421     }
422   DEBUG_start_sourceline = start;
423   DEBUG_end_sourceline = end;
424 }
425
426 DBG_ADDR DEBUG_LastDisassemble={0,0};
427
428 static int
429 _disassemble(DBG_ADDR *addr)
430 {
431    char ch;
432
433    DEBUG_PrintAddress( addr, DEBUG_CurrThread->dbg_mode, TRUE );
434    DEBUG_Printf(DBG_CHN_MESG,": ");
435    if (!DEBUG_READ_MEM_VERBOSE((void*)DEBUG_ToLinear(addr), &ch, sizeof(ch))) return 0;
436    DEBUG_Disasm( addr, TRUE );
437    DEBUG_Printf(DBG_CHN_MESG,"\n");
438    return 1;
439 }
440
441 void
442 _disassemble_fixaddr(DBG_VALUE *value) {
443     DWORD seg2;
444     struct datatype *testtype;
445
446     assert(value->cookie == DV_TARGET || value->cookie == DV_HOST);
447
448 #ifdef __i386__
449     DEBUG_FixAddress(&value->addr, DEBUG_context.SegCs);
450 #endif
451
452     if( value->type != NULL )
453       {
454         if( value->type == DEBUG_TypeIntConst )
455           {
456             /*
457              * We know that we have the actual offset stored somewhere
458              * else in 32-bit space.  Grab it, and we
459              * should be all set.
460              */
461             seg2 = value->addr.seg;
462             value->addr.seg = 0;
463             value->addr.off = DEBUG_GetExprValue(value, NULL);
464             value->addr.seg = seg2;
465           }
466         else
467           {
468             DEBUG_TypeDerefPointer(value, &testtype);
469             if( testtype != NULL || value->type == DEBUG_TypeIntConst )
470                 value->addr.off = DEBUG_GetExprValue(value, NULL);
471           }
472       }
473     else if (!value->addr.seg && !value->addr.off)
474     {
475         DEBUG_Printf(DBG_CHN_MESG,"Invalid expression\n");
476         return;
477     }
478 }
479
480 void
481 DEBUG_Disassemble(const DBG_VALUE *xstart,const DBG_VALUE *xend,int offset)
482 {
483   int i;
484   DBG_ADDR      last;
485   DBG_VALUE     end,start;
486
487   if (xstart) {
488     start = *xstart;
489     _disassemble_fixaddr(&start);
490   }
491   if (xend) {
492     end = *xend;
493     _disassemble_fixaddr(&end);
494   }
495   if (!xstart && !xend) {
496     last = DEBUG_LastDisassemble;
497     if (!last.seg && !last.off)
498       DEBUG_GetCurrentAddress( &last );
499
500     for (i=0;i<offset;i++)
501       if (!_disassemble(&last)) break;
502     DEBUG_LastDisassemble = last;
503     return;
504   }
505   last = start.addr;
506   if (!xend) {
507     for (i=0;i<offset;i++)
508       if (!_disassemble(&last)) break;
509     DEBUG_LastDisassemble = last;
510     return;
511   }
512   while (last.off <= end.addr.off)
513     if (!_disassemble(&last)) break;
514   DEBUG_LastDisassemble = last;
515   return;
516 }
517
518
519
520 #if 0
521 main(void)
522 {
523   int i, j;
524   DEBUG_AddPath("../../de");
525   while(1==1)
526     {
527       fscanf(stdin,"%d %d", &i, &j);
528       DEBUG_DisplaySource("dumpexe.c", i, j);
529     }
530   return 0;
531 }
532 #endif