Buffer overflow hunt: removing strcpy, strcat, sprintf
[fw/sdcc] / src / asm.c
1 /** @file asm.c
2     Provides output functions that modify the output string
3     based on the input tokens and the assembler token mapping
4     specification loaded.
5
6     Note that the functions below only handle digit format modifiers.
7     eg %02X is ok, but %lu and %.4u will fail.
8 */
9 #include "common.h"
10 #include "asm.h"
11
12 #if defined __MINGW32__
13    // for O_BINARY in _pipe()
14 #  include <fcntl.h>
15 #elif !defined(__BORLANDC__) && !defined(_MSC_VER)
16    // for pipe and close
17 #  include <unistd.h>
18 #endif
19
20 /* A 'token' is like !blah or %24f and is under the programmers
21    control. */
22 #define MAX_TOKEN_LEN           64
23
24 static hTab *_h;
25
26 char *
27 FileBaseName (char *fileFullName)
28 {
29   char *p = fileFullName;
30
31   if (!fileFullName) {
32     return "unknown";
33   }
34
35   while (*fileFullName)
36     {
37       if ((*fileFullName == '/') || (*fileFullName == '\\') || (*fileFullName == ':'))
38         {
39           p = fileFullName;
40           p++;
41         }
42       fileFullName++;
43     }
44   return p;
45 }
46
47 static const char *
48 _findMapping (const char *szKey)
49 {
50   return shash_find (_h, szKey);
51 }
52
53 // Append a string onto another, and update the pointer to the end of
54 // the new string.
55 static char *
56 _appendAt (char *at, char *onto, const char *sz, size_t *max)
57 {
58   wassert (at && onto && sz);
59   strncpyz (at, sz, *max);
60   *max -= strlen (at);
61   return at + strlen (at);
62 }
63
64 void 
65 tvsprintf (char *buffer, size_t len, const char *format, va_list ap)
66 {
67   // Under Linux PPC va_list is a structure instead of a primitive type,
68   // and doesnt like being passed around.  This version turns everything
69   // into one function.
70
71   // Supports:
72   //  !tokens
73   //  %[CIFN] - special formats with no argument (ie list isnt touched)
74   //  All of the system formats
75
76   // This is acheived by expanding the tokens and zero arg formats into
77   // one big format string, which is passed to the native printf.
78   static int    count;
79   char          noTokens[INITIAL_INLINEASM];
80   char          newFormat[INITIAL_INLINEASM];
81   char          *pInto = noTokens;
82   size_t        pIntoLen = sizeof(noTokens);
83   char          *p;
84   char          token[MAX_TOKEN_LEN];
85   const char    *sz = format;
86
87   // NULL terminate it to let strlen work.
88   *pInto = '\0';
89
90   /* First pass: expand all of the macros */
91   while (pIntoLen && *sz)
92     {
93       if (*sz == '!')
94         {
95           /* Start of a token.  Search until the first
96              [non alpha, *] and call it a token. */
97           const char *t;
98           p = token;
99           sz++;
100           while (isalpha (*sz) || *sz == '*')
101             {
102               *p++ = *sz++;
103             }
104           *p = '\0';
105           /* Now find the token in the token list */
106           if ((t = _findMapping (token)))
107             {
108               pInto = _appendAt (pInto, noTokens, t, &pIntoLen);
109             }
110           else
111             {
112               fprintf (stderr, "Cant find token \"%s\"\n", token);
113               wassert (0);
114             }
115         }
116       else
117         {
118           *pInto++ = *sz++;
119           pIntoLen--;
120         }
121     }
122
123   if (!pIntoLen)
124   {
125       fprintf(stderr,
126                 "Internal error: tvsprintf overflowed on pass one.\n");
127       // Might as well go on...
128   }
129     
130   *pInto = '\0';
131
132   /* Second pass: Expand any macros that we own */
133   sz = noTokens;
134   pInto = newFormat;
135   pIntoLen = sizeof(newFormat);
136
137   while (pIntoLen && *sz)
138     {
139       if (*sz == '%')
140         {
141           // See if its one that we handle.
142           sz++;
143           switch (*sz)
144             {
145             case 'C':
146               // Code segment name.
147               pInto = _appendAt (pInto, newFormat, CODE_NAME, &pIntoLen);
148               sz++;
149               break;
150             case 'F':
151               // Source file name.
152               pInto = _appendAt (pInto, newFormat, fullSrcFileName, &pIntoLen);
153               sz++;
154               break;
155             case 'N':
156               // Current function name.
157               pInto = _appendAt (pInto, newFormat, currFunc->rname, &pIntoLen);
158               sz++;
159               break;
160             case 'I':
161               {
162                 // Unique ID.
163                 char id[20];
164                 SNPRINTF (id, sizeof(id), "%u", ++count);
165                 pInto = _appendAt (pInto, newFormat, id, &pIntoLen);
166                 sz++;
167                 break;
168               }
169             default:
170               // Not one of ours.  Copy until the end.
171               *pInto++ = '%';
172               pIntoLen--;
173               while (pIntoLen && !isalpha (*sz))
174                 {
175                   *pInto++ = *sz++;
176                   pIntoLen--;
177                 }
178                 if (pIntoLen)
179                 {
180                     *pInto++ = *sz++;
181                     pIntoLen--;
182                 }
183             }
184         }
185       else
186         {
187           *pInto++ = *sz++;
188           pIntoLen--;
189         }
190     }
191
192   if (!pIntoLen)
193   {
194       fprintf(stderr,
195                 "Internal error: tvsprintf overflowed on pass two.\n");
196       // Might as well go on...
197   }    
198     
199   *pInto = '\0';
200
201   // Now do the actual printing
202 #if defined(HAVE_VSNPRINTF)
203     {
204         int wrlen;
205         wrlen = vsnprintf (buffer, len, newFormat, ap);
206         
207         if (wrlen < 0 || wrlen >= len)
208         {
209             fprintf(stderr, "Internal error: tvsprintf truncated.\n");
210         }
211     }
212     
213 #else    
214     vsprintf (buffer, newFormat, ap);
215     if (strlen(buffer) >= len)
216     {
217         fprintf(stderr, "Internal error: tvsprintf overflowed.\n");
218     }
219 #endif    
220 }
221
222 void 
223 tfprintf (FILE * fp, const char *szFormat,...)
224 {
225   va_list ap;
226   char buffer[INITIAL_INLINEASM];
227
228   va_start (ap, szFormat);
229   tvsprintf (buffer, INITIAL_INLINEASM, szFormat, ap);
230   va_end(ap);
231   fputs (buffer, fp);
232 }
233
234 void 
235 tsprintf (char *buffer, size_t len, const char *szFormat,...)
236 {
237   va_list ap;
238   va_start (ap, szFormat);
239   tvsprintf (buffer, len, szFormat, ap);
240   va_end(ap);
241 }
242
243 void 
244 asm_addTree (const ASM_MAPPINGS * pMappings)
245 {
246   const ASM_MAPPING *pMap;
247
248   /* Traverse down first */
249   if (pMappings->pParent)
250     asm_addTree (pMappings->pParent);
251   pMap = pMappings->pMappings;
252   while (pMap->szKey && pMap->szValue) {
253       shash_add (&_h, pMap->szKey, pMap->szValue);
254       pMap++;
255   }
256 }
257
258 /*-----------------------------------------------------------------*/
259 /* printILine - return the readable i-code for this ic             */
260 /*                                                                 */
261 /* iCodePrint wants a file stream so we need a pipe to fool it     */
262 /*-----------------------------------------------------------------*/
263 static char verbalICode[1024];
264
265 char *printILine (iCode *ic) {
266   int filedes[2];
267   FILE *pipeStream;
268   iCodeTable *icTab=getTableEntry(ic->op);
269   
270 #if defined __MINGW32__
271   assert(_pipe(filedes, 256, O_BINARY)!=-1); // forget it
272 #else
273   assert(pipe(filedes)!=-1); // forget it
274 #endif
275
276   // stuff the pipe with the readable icode
277   pipeStream=fdopen(filedes[1],"w");
278   icTab->iCodePrint(pipeStream, ic, icTab->printName);
279   // it really needs an extra push
280   fflush(pipeStream);
281   // now swallow it
282   pipeStream=fdopen(filedes[0],"r");
283   fgets(verbalICode, sizeof(verbalICode), pipeStream);
284   // clean up the mess, we'll return here for all icodes!!
285   assert(!close (filedes[0]));
286   assert(!close (filedes[1]));
287   // kill the trailing NL
288   verbalICode[strlen(verbalICode)-1]='\0';
289   // and throw it up
290   return verbalICode;
291 }
292
293 /*-----------------------------------------------------------------*/
294 /* printCLine - return the c-code for this lineno                  */
295 /*-----------------------------------------------------------------*/
296 static FILE *inFile=NULL;
297 static char inLineString[1024];
298 static int inLineNo=0;
299 static char lastSrcFile[PATH_MAX];
300 int rewinds=0;
301
302 char *printCLine (char *srcFile, int lineno) {
303   char *ilsP=inLineString;
304
305   if (inFile) {
306     if (strcmp (lastSrcFile, srcFile) != 0) {
307       fclose (inFile);
308       inFile = NULL;
309       inLineNo = 0;
310     }
311   }
312   if (!inFile) {
313     inFile=fopen(srcFile, "r");
314     if (!inFile) {
315       perror ("printCLine");
316       exit (1);
317     }
318     strncpyz (lastSrcFile, srcFile, PATH_MAX);
319   }
320   if (lineno<inLineNo) {
321     fseek (inFile, 0, SEEK_SET);
322     inLineNo=0;
323     rewinds++;
324   }
325   while (fgets (inLineString, 1024, inFile)) {
326     inLineNo++;
327     if (inLineNo==lineno) {
328       // remove the trailing NL
329       inLineString[strlen(inLineString)-1]='\0';
330       break;
331     }
332   }
333   while (isspace ((int)*ilsP))
334     ilsP++;
335
336   return ilsP;
337 }
338
339 static const ASM_MAPPING _asxxxx_mapping[] =
340 {
341   {"labeldef", "%s::"},
342   {"slabeldef", "%s:"},
343   {"tlabeldef", "%05d$:"},
344   {"tlabel", "%05d$"},
345   {"immed", "#"},
346   {"zero", "#0x00"},
347   {"one", "#0x01"},
348   {"area", ".area %s"},
349   {"areacode", ".area %s"},
350   {"areadata", ".area %s"},
351   {"areahome", ".area %s"},
352   {"ascii", ".ascii \"%s\""},
353   {"ds", ".ds %d"},
354   {"db", ".db"},
355   {"dbs", ".db %s"},
356   {"dw", ".dw"},
357   {"dws", ".dw %s"},
358   {"constbyte", "0x%02X"},
359   {"constword", "0x%04X"},
360   {"immedword", "#0x%04X"},
361   {"immedbyte", "#0x%02X"},
362   {"hashedstr", "#%s"},
363   {"lsbimmeds", "#<%s"},
364   {"msbimmeds", "#>%s"},
365   {"module", ".module %s"},
366   {"global", ".globl %s"},
367   {"fileprelude", ""},
368   {"functionheader",
369    "; ---------------------------------\n"
370    "; Function %s\n"
371    "; ---------------------------------"
372   },
373   {"functionlabeldef", "%s:"},
374   {"bankimmeds", "0     ; PENDING: bank support"},
375   {"los","(%s & 0xFF)"},
376   {"his","(%s >> 8)"},
377   {"hihis","(%s >> 16)"},
378   {"hihihis","(%s >> 24)"},
379   {"lod","(%d & 0xFF)"},
380   {"hid","(%d >> 8)"},
381   {"hihid","(%d >> 16)"},
382   {"hihihid","(%d >> 24)"},
383   {"lol","(%05d$ & 0xFF)"},
384   {"hil","(%05d$ >> 8)"},
385   {"hihil","(%05d$ >> 16)"},
386   {"hihihil","(%05d$ >> 24)"},
387   {"equ","="},
388   {NULL, NULL}
389 };
390
391 static const ASM_MAPPING _gas_mapping[] =
392 {
393   {"labeldef", "%s::"},
394   {"slabeldef", "%s:"},
395   {"tlabeldef", "%05d$:"},
396   {"tlabel", "%05d$"},
397   {"immed", "#"},
398   {"zero", "#0x00"},
399   {"one", "#0x01"},
400   {"area", ".section %s"},
401   {"areacode", ".section %s"},
402   {"areadata", ".section %s"},
403   {"areahome", ".section %s"},
404   {"ascii", ".ascii \"%s\""},
405   {"ds", ".ds %d"},
406   {"db", ".db"},
407   {"dbs", ".db %s"},
408   {"dw", ".dw"},
409   {"dws", ".dw %s"},
410   {"constbyte", "0x%02X"},
411   {"constword", "0x%04X"},
412   {"immedword", "#0x%04X"},
413   {"immedbyte", "#0x%02X"},
414   {"hashedstr", "#%s"},
415   {"lsbimmeds", "#<%s"},
416   {"msbimmeds", "#>%s"},
417   {"module", ".file \"%s.c\""},
418   {"global", ".globl %s"},
419   {"extern", ".globl %s"},
420   {"fileprelude", ""},
421   {"functionheader",
422    "; ---------------------------------\n"
423    "; Function %s\n"
424    "; ---------------------------------"
425   },
426   {"functionlabeldef", "%s:"},
427   {"bankimmeds", "0     ; PENDING: bank support"},  
428   {NULL, NULL}
429 };
430
431 static const ASM_MAPPING _a390_mapping[] =
432 {
433   {"labeldef", "%s:"},
434   {"slabeldef", "%s:"},
435   {"tlabeldef", "L%05d:"},
436   {"tlabel", "L%05d"},
437   {"immed", "#"},
438   {"zero", "#0"},
439   {"one", "#1"},
440   {"area", "; SECTION NOT SUPPORTED"},
441   {"areacode", "; SECTION NOT SUPPORTED"},
442   {"areadata", "; SECTION NOT SUPPORTED"},
443   {"areahome", "; SECTION NOT SUPPORTED"},
444   {"ascii", "db \"%s\""},
445   {"ds", "; STORAGE NOT SUPPORTED"},
446   {"db", "db"},
447   {"dbs", "db \"%s\""},
448   {"dw", "dw"},
449   {"dws", "dw %s"},
450   {"constbyte", "0%02xh"},
451   {"constword", "0%04xh"},
452   {"immedword", "#0%04Xh"},
453   {"immedbyte", "#0%02Xh"},
454   {"hashedstr", "#%s"},
455   {"lsbimmeds", "#<%s"},
456   {"msbimmeds", "#>%s"},
457   {"module", "; .file \"%s.c\""},
458   {"global", "; .globl %s"},
459   {"fileprelude", ""},
460   {"functionheader",
461    "; ---------------------------------\n"
462    "; Function %s\n"
463    "; ---------------------------------"
464   },
465   {"functionlabeldef", "%s:"},
466   {"bankimmeds", "0     ; PENDING: bank support"},  
467   {"los","(%s & 0FFh)"},
468   {"his","((%s / 256) & 0FFh)"},
469   {"hihis","((%s / 65536) & 0FFh)"},
470   {"hihihis","((%s / 16777216) & 0FFh)"},
471   {"lod","(%d & 0FFh)"},
472   {"hid","((%d / 256) & 0FFh)"},
473   {"hihid","((%d / 65536) & 0FFh)"},
474   {"hihihid","((%d / 16777216) & 0FFh)"},
475   {"lol","(L%05d & 0FFh)"},
476   {"hil","((L%05d / 256) & 0FFh)"},
477   {"hihil","((L%05d / 65536) & 0FFh)"},
478   {"hihihil","((L%09d / 16777216) & 0FFh)"},
479   {"equ"," equ"},
480   {NULL, NULL}
481 };
482
483 static const ASM_MAPPING _xa_asm_mapping[] =
484 {
485   {"labeldef", "%s:"},
486   {"slabeldef", "%s:"},
487   {"tlabeldef", "L%05d:"},
488   {"tlabel", "L%05d"},
489   {"immed", "#"},
490   {"zero", "#0"},
491   {"one", "#1"},
492   {"area", ".area %s"},
493   {"areacode", ".area %s"},
494   {"areadata", ".area %s"},
495   {"areahome", ".area %s"},
496   {"ascii", ".db \"%s\""},
497   {"ds", ".ds %d"},
498   {"db", ".db"},
499   {"dbs", ".db \"%s\""},
500   {"dw", ".dw"},
501   {"dws", ".dw %s"},
502   {"constbyte", "0x%02x"},
503   {"constword", "0x%04x"},
504   {"immedword", "0x%04x"},
505   {"immedbyte", "0x%02x"},
506   {"hashedstr", "#%s"},
507   {"lsbimmeds", "#<%s"},
508   {"msbimmeds", "#>%s"},
509   {"module", "; .module %s"},
510   {"global", ".globl %s"},
511   {"fileprelude", ""},
512   {"functionheader",
513    "; ---------------------------------\n"
514    "; Function %s\n"
515    "; ---------------------------------"
516   },
517   {"functionlabeldef", "%s:"},
518   {"bankimmeds", "0     ; PENDING: bank support"},  
519   {"los","(%s & 0FFh)"},
520   {"his","((%s / 256) & 0FFh)"},
521   {"hihis","((%s / 65536) & 0FFh)"},
522   {"hihihis","((%s / 16777216) & 0FFh)"},
523   {"lod","(%d & 0FFh)"},
524   {"hid","((%d / 256) & 0FFh)"},
525   {"hihid","((%d / 65536) & 0FFh)"},
526   {"hihihid","((%d / 16777216) & 0FFh)"},
527   {"lol","(L%05d & 0FFh)"},
528   {"hil","((L%05d / 256) & 0FFh)"},
529   {"hihil","((L%05d / 65536) & 0FFh)"},
530   {"hihihil","((L%09d / 16777216) & 0FFh)"},
531   {"equ"," equ"},
532   {NULL, NULL}
533 };
534
535 const ASM_MAPPINGS asm_asxxxx_mapping =
536 {
537   NULL,
538   _asxxxx_mapping
539 };
540
541 const ASM_MAPPINGS asm_gas_mapping =
542 {
543   NULL,
544   _gas_mapping
545 };
546
547 const ASM_MAPPINGS asm_a390_mapping =
548 {
549   NULL,
550   _a390_mapping
551 };
552
553 const ASM_MAPPINGS asm_xa_asm_mapping =
554 {
555   NULL,
556   _xa_asm_mapping
557 };