minor fixes
[ia32rtools.git] / tools / cvt_data.c
1 /*
2  * ia32rtools
3  * (C) notaz, 2013-2015
4  *
5  * This work is licensed under the terms of 3-clause BSD license.
6  * See COPYING file in the top-level directory.
7  */
8
9 #define _GNU_SOURCE
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <stdint.h>
14 #include <inttypes.h>
15
16 #include "my_assert.h"
17 #include "my_str.h"
18 #include "common.h"
19
20 #include "protoparse.h"
21
22 static const char *asmfn;
23 static int asmln;
24
25 static const struct parsed_proto *g_func_sym_pp;
26 static char g_comment[256];
27 static int g_warn_cnt;
28 static int g_cconv_novalidate;
29 static int g_arm_mode;
30
31 // note: must be in ascending order
32 enum dx_type {
33   DXT_UNSPEC,
34   DXT_BYTE,
35   DXT_WORD,
36   DXT_DWORD,
37   DXT_QUAD,
38   DXT_TEN,
39 };
40
41 #define anote(fmt, ...) \
42         printf("%s:%d: note: " fmt, asmfn, asmln, ##__VA_ARGS__)
43 #define awarn(fmt, ...) do { \
44         printf("%s:%d: warning: " fmt, asmfn, asmln, ##__VA_ARGS__); \
45   if (++g_warn_cnt == 10) { \
46     fcloseall(); \
47           exit(1); \
48   } \
49 } while (0)
50 #define aerr(fmt, ...) do { \
51         printf("%s:%d: error: " fmt, asmfn, asmln, ##__VA_ARGS__); \
52   fcloseall(); \
53         exit(1); \
54 } while (0)
55
56 #include "masm_tools.h"
57
58 static char *next_word_s(char *w, size_t wsize, char *s)
59 {
60   int quote = 0;
61         size_t i;
62
63         s = sskip(s);
64
65         for (i = 0; i < wsize - 1; i++) {
66     if (s[i] == '\'')
67       quote ^= 1;
68                 if (s[i] == 0 || (!quote && (my_isblank(s[i]) || s[i] == ',')))
69                         break;
70                 w[i] = s[i];
71         }
72         w[i] = 0;
73
74         if (s[i] != 0 && !my_isblank(s[i]) && s[i] != ',')
75                 printf("warning: '%s' truncated\n", w);
76
77         return s + i;
78 }
79
80 static void next_section(FILE *fasm, char *name)
81 {
82   char words[2][256];
83   char line[256];
84   int wordc;
85   char *p;
86
87   name[0] = 0;
88
89   while (my_fgets(line, sizeof(line), fasm))
90   {
91     wordc = 0;
92     asmln++;
93
94     p = sskip(line);
95     if (*p == 0)
96       continue;
97
98     if (*p == ';')
99       continue;
100
101     for (wordc = 0; wordc < ARRAY_SIZE(words); wordc++) {
102       p = sskip(next_word(words[wordc], sizeof(words[0]), p));
103       if (*p == 0 || *p == ';') {
104         wordc++;
105         break;
106       }
107     }
108
109     if (wordc < 2)
110       continue;
111
112     if (!IS(words[1], "segment"))
113       continue;
114
115     strcpy(name, words[0]);
116     break;
117   }
118 }
119
120 static enum dx_type parse_dx_directive(const char *name)
121 {
122   if (IS(name, "dd"))
123     return DXT_DWORD;
124   if (IS(name, "dw"))
125     return DXT_WORD;
126   if (IS(name, "db"))
127     return DXT_BYTE;
128   if (IS(name, "dq"))
129     return DXT_QUAD;
130   if (IS(name, "dt"))
131     return DXT_TEN;
132
133   return DXT_UNSPEC;
134 }
135
136 static const char *type_name(enum dx_type type)
137 {
138   switch (type) {
139   case DXT_BYTE:
140     return ".byte";
141   case DXT_WORD:
142     return ".hword";
143   case DXT_DWORD:
144     return ".long";
145   case DXT_QUAD:
146     return ".quad";
147   case DXT_TEN:
148     return ".tfloat";
149   case DXT_UNSPEC:
150     break;
151   }
152   return "<bad>";
153 }
154
155 static const char *type_name_float(enum dx_type type)
156 {
157   switch (type) {
158   case DXT_DWORD:
159     return ".float";
160   case DXT_QUAD:
161     return ".double";
162   case DXT_TEN:
163     return ".tfloat";
164   default:
165     break;
166   }
167   return "<bad_float>";
168 }
169
170 static int type_size(enum dx_type type)
171 {
172   switch (type) {
173   case DXT_BYTE:
174     return 1;
175   case DXT_WORD:
176     return 2;
177   case DXT_DWORD:
178     return 4;
179   case DXT_QUAD:
180     return 8;
181   case DXT_TEN:
182     return 10;
183   case DXT_UNSPEC:
184     break;
185   }
186   return -1;
187 }
188
189 static char *escape_string(char *s)
190 {
191   char buf[256];
192   char *t = buf;
193
194   for (; *s != 0; s++) {
195     if (*s == '"') {
196       strcpy(t, "\\\"");
197       t += strlen(t);
198       continue;
199     }
200     if (*s == '\\') {
201       strcpy(t, "\\\\");
202       t += strlen(t);
203       continue;
204     }
205     *t++ = *s;
206   }
207   *t++ = *s;
208   if (t - buf > sizeof(buf))
209     aerr("string is too long\n");
210   return strcpy(s, buf);
211 }
212
213 static void sprint_pp_short(const struct parsed_proto *pp, char *buf,
214   size_t buf_size)
215 {
216   char *p = buf;
217   size_t l;
218   int i;
219
220   if (pp->ret_type.is_ptr)
221     *p++ = 'p';
222   else if (IS(pp->ret_type.name, "void"))
223     *p++ = 'v';
224   else
225     *p++ = 'i';
226   *p++ = '(';
227   l = 2;
228
229   for (i = 0; i < pp->argc; i++) {
230     if (pp->arg[i].reg != NULL)
231       snprintf(buf + l, buf_size - l, "%s%s",
232         i == 0 ? "" : ",", pp->arg[i].reg);
233     else
234       snprintf(buf + l, buf_size - l, "%sa%d",
235         i == 0 ? "" : ",", i + 1);
236     l = strlen(buf);
237   }
238   snprintf(buf + l, buf_size - l, ")");
239 }
240
241 static const struct parsed_proto *check_var(FILE *fhdr,
242   const char *sym, const char *varname, int is_export)
243 {
244   const struct parsed_proto *pp, *pp_sym;
245   char fp_sym[256], fp_var[256], *p;
246   int i;
247
248   pp = proto_parse(fhdr, varname, 1);
249   if (pp == NULL) {
250     if (IS_START(varname, "sub_"))
251       awarn("sub_ sym missing proto: '%s'\n", varname);
252     return NULL;
253   }
254
255   if (is_export)
256     return NULL;
257   if (!pp->is_func && !pp->is_fptr)
258     return NULL;
259
260   pp_print(fp_var, sizeof(fp_var), pp);
261
262   if (pp->argc_reg == 0)
263     goto check_sym;
264   if (pp->argc_reg == 1 && pp->argc_stack == 0
265     && IS(pp->arg[0].reg, "ecx"))
266   {
267     goto check_sym;
268   }
269   if (!g_cconv_novalidate
270     && (pp->argc_reg != 2
271        || !IS(pp->arg[0].reg, "ecx")
272        || !IS(pp->arg[1].reg, "edx")))
273   {
274     awarn("unhandled reg call: %s\n", fp_var);
275   }
276
277 check_sym:
278   // fptrs must use 32bit args, callsite might have no information and
279   // lack a cast to smaller types, which results in incorrectly masked
280   // args passed (callee may assume masked args, it does on ARM)
281   for (i = 0; i < pp->argc; i++) {
282     if (pp->arg[i].type.is_ptr)
283       continue;
284     p = pp->arg[i].type.name;
285     if (strstr(p, "int8") || strstr(p, "int16")
286       || strstr(p, "char") || strstr(p, "short"))
287     {
288       awarn("reference to %s with arg%d '%s'\n", pp->name, i + 1, p);
289     }
290   }
291
292   sprint_pp_short(pp, g_comment, sizeof(g_comment));
293
294   if (sym != NULL) {
295     g_func_sym_pp = NULL;
296     pp_sym = proto_parse(fhdr, sym, 1);
297     if (pp_sym == NULL)
298       return pp;
299     if (!pp_sym->is_fptr)
300       aerr("func ptr data, but label '%s' !is_fptr\n", pp_sym->name);
301     g_func_sym_pp = pp_sym;
302   }
303   else {
304     pp_sym = g_func_sym_pp;
305     if (pp_sym == NULL)
306       return pp;
307   }
308
309   if (!pp_compatible_func(pp_sym, pp)) {
310     pp_print(fp_sym, sizeof(fp_sym), pp_sym);
311     anote("entry: %s\n", fp_var);
312     anote("label: %s\n", fp_sym);
313     awarn("^ mismatch\n");
314   }
315
316   return pp;
317 }
318
319 static void output_decorated_pp(FILE *fout,
320   const struct parsed_proto *pp)
321 {
322   if (pp->name[0] != '_')
323     fprintf(fout, pp->is_fastcall ? "@" : "_");
324   fprintf(fout, "%s", pp->name);
325   if (pp->is_stdcall && pp->argc > 0)
326     fprintf(fout, "@%d", pp->argc * 4);
327 }
328
329 static int align_value(int src_val)
330 {
331   if (src_val <= 0) {
332     awarn("bad align: %d\n", src_val);
333     src_val = 1;
334   }
335   if (!g_arm_mode)
336     return src_val;
337
338   return __builtin_ffs(src_val) - 1;
339 }
340
341 static int cmpstringp(const void *p1, const void *p2)
342 {
343   return strcmp(*(char * const *)p1, *(char * const *)p2);
344 }
345
346 /* XXX: maybe move to external file? */
347 static const char *unwanted_syms[] = {
348   "aRuntimeError",
349   "aTlossError",
350   "aSingError",
351   "aDomainError",
352   "aR6029ThisAppli",
353   "aR6028UnableToI",
354   "aR6027NotEnough",
355   "aR6026NotEnough",
356   "aR6025PureVirtu",
357   "aR6024NotEnough",
358   "aR6019UnableToO",
359   "aR6018Unexpecte",
360   "aR6017Unexpecte",
361   "aR6016NotEnough",
362   "aAbnormalProgra",
363   "aR6009NotEnough",
364   "aR6008NotEnough",
365   "aR6002FloatingP",
366   "aMicrosoftVisua",
367   "aRuntimeErrorPr",
368   "aThisApplicatio",
369   "aMicrosoftFindF",
370   "aMicrosoftOffic",
371 };
372
373 static int is_unwanted_sym(const char *sym)
374 {
375   return bsearch(&sym, unwanted_syms, ARRAY_SIZE(unwanted_syms),
376     sizeof(unwanted_syms[0]), cmpstringp) != NULL;
377 }
378
379 int main(int argc, char *argv[])
380 {
381   FILE *fout, *fasm, *fhdr = NULL, *frlist;
382   const struct parsed_proto *pp;
383   int no_decorations = 0;
384   int header_mode = 0;
385   int maybe_func_table;
386   int in_export_table;
387   int rm_labels_lines;
388   int is_zero_val;
389   char comment_char = '#';
390   char words[20][256];
391   char word[256];
392   char line[256];
393   char last_sym[32];
394   unsigned long val;
395   unsigned long cnt;
396   uint64_t val64;
397   const char *sym;
398   enum dx_type type;
399   char **pub_syms;
400   int pub_sym_cnt = 0;
401   int pub_sym_alloc;
402   char **rlist;
403   int rlist_cnt = 0;
404   int rlist_alloc;
405   int is_ro = 0;
406   int is_label;
407   int is_bss;
408   int wordc;
409   int first;
410   int arg_out;
411   int arg = 1;
412   int len;
413   int w, i;
414   char *p;
415   char *p2;
416
417   if (argc < 4) {
418     // -nd: no symbol decorations
419     printf("usage:\n%s [-nd] [-i] [-a] <.s> <.asm> <hdrf> [rlist]*\n"
420            "%s -hdr <.h> <.asm>\n",
421       argv[0], argv[0]);
422     return 1;
423   }
424
425   for (arg = 1; arg < argc; arg++) {
426     if (IS(argv[arg], "-nd"))
427       no_decorations = 1;
428     else if (IS(argv[arg], "-i"))
429       g_cconv_novalidate = 1;
430     else if (IS(argv[arg], "-a")) {
431       comment_char = '@';
432       g_arm_mode = 1;
433     }
434     else if (IS(argv[arg], "-hdr"))
435       header_mode = 1;
436     else
437       break;
438   }
439
440   arg_out = arg++;
441
442   asmfn = argv[arg++];
443   fasm = fopen(asmfn, "r");
444   my_assert_not(fasm, NULL);
445
446   if (!header_mode) {
447     hdrfn = argv[arg++];
448     fhdr = fopen(hdrfn, "r");
449     my_assert_not(fhdr, NULL);
450   }
451
452   fout = fopen(argv[arg_out], "w");
453   my_assert_not(fout, NULL);
454
455   pub_sym_alloc = 64;
456   pub_syms = malloc(pub_sym_alloc * sizeof(pub_syms[0]));
457   my_assert_not(pub_syms, NULL);
458
459   rlist_alloc = 64;
460   rlist = malloc(rlist_alloc * sizeof(rlist[0]));
461   my_assert_not(rlist, NULL);
462
463   for (; arg < argc; arg++) {
464     frlist = fopen(argv[arg], "r");
465     my_assert_not(frlist, NULL);
466
467     while (my_fgets(line, sizeof(line), frlist)) {
468       p = sskip(line);
469       if (*p == 0 || *p == ';' || *p == '#')
470         continue;
471
472       p = next_word(words[0], sizeof(words[0]), p);
473       if (words[0][0] == 0)
474         continue;
475
476       if (rlist_cnt >= rlist_alloc) {
477         rlist_alloc = rlist_alloc * 2 + 64;
478         rlist = realloc(rlist, rlist_alloc * sizeof(rlist[0]));
479         my_assert_not(rlist, NULL);
480       }
481       rlist[rlist_cnt++] = strdup(words[0]);
482     }
483
484     fclose(frlist);
485     frlist = NULL;
486   }
487
488   if (rlist_cnt > 0)
489     qsort(rlist, rlist_cnt, sizeof(rlist[0]), cmpstringp);
490
491   qsort(unwanted_syms, ARRAY_SIZE(unwanted_syms),
492     sizeof(unwanted_syms[0]), cmpstringp);
493
494   while (1) {
495     last_sym[0] = 0;
496     g_func_sym_pp = NULL;
497     maybe_func_table = 0;
498     in_export_table = 0;
499     rm_labels_lines = 0;
500
501     next_section(fasm, line);
502     if (feof(fasm))
503       break;
504     if (IS(line + 1, "text"))
505       continue;
506
507     if (IS(line + 1, "rdata")) {
508       is_ro = 1;
509       if (!header_mode)
510         fprintf(fout, "\n.section .rodata\n");
511     }
512     else if (IS(line + 1, "data")) {
513       is_ro = 0;
514       if (!header_mode)
515         fprintf(fout, "\n.data\n");
516     }
517     else
518       aerr("unhandled section: '%s'\n", line);
519
520     if (!header_mode)
521       fprintf(fout, ".align %d\n", align_value(4));
522
523     while (my_fgets(line, sizeof(line), fasm))
524     {
525       is_zero_val = 0;
526       sym = NULL;
527       asmln++;
528
529       p = sskip(line);
530       if (*p == 0)
531         continue;
532
533       if (*p == ';') {
534         if (IS_START(p, ";org") && sscanf(p + 5, "%Xh", &i) == 1) {
535           // ;org is only seen at section start, so assume . addr 0
536           i &= 0xfff;
537           if (i != 0 && !header_mode)
538             fprintf(fout, "\t\t  .skip 0x%x\n", i);
539         }
540         else if (IS_START(p, "; Export Address"))
541           in_export_table = 1;
542         else if (IS_START(p, "; Export"))
543           in_export_table = 0;
544         continue;
545       }
546
547       for (wordc = 0; wordc < ARRAY_SIZE(words); wordc++) {
548         p = sskip(next_word_s(words[wordc], sizeof(words[0]), p));
549         if (*p == 0 || *p == ';') {
550           wordc++;
551           break;
552         }
553         if (*p == ',') {
554           p = sskip(p + 1);
555         }
556       }
557
558       if (*p == ';') {
559         p = sskip(p + 1);
560         if (IS_START(p, "sctclrtype")) {
561           maybe_func_table = 0;
562           g_func_sym_pp = NULL;
563         }
564       }
565
566       if (wordc == 2 && IS(words[1], "ends"))
567         break;
568       if (wordc <= 2 && IS(words[0], "end"))
569         break;
570       if (wordc < 2)
571         aerr("unhandled: '%s'\n", words[0]);
572
573       // don't cares
574       if (IS(words[0], "assume"))
575         continue;
576
577       if (IS(words[0], "align")) {
578         if (header_mode)
579           continue;
580
581         val = parse_number(words[1], 0);
582         fprintf(fout, "\t\t  .align %d", align_value(val));
583         goto fin;
584       }
585
586       if (IS(words[0], "public")) {
587         // skip, sym should appear in header anyway
588         continue;
589       }
590
591       w = 1;
592       type = parse_dx_directive(words[0]);
593       if (type == DXT_UNSPEC) {
594         type = parse_dx_directive(words[1]);
595         sym = words[0];
596         w = 2;
597       }
598       if (type == DXT_UNSPEC)
599         aerr("unhandled decl: '%s %s'\n", words[0], words[1]);
600
601       if (sym != NULL)
602       {
603         if (header_mode) {
604           int is_str = 0;
605
606           fprintf(fout, "extern ");
607           if (is_ro)
608             fprintf(fout, "const ");
609
610           switch (type) {
611           case DXT_BYTE:
612             for (i = w; i < wordc; i++)
613               if (words[i][0] == '\'')
614                 is_str = 1;
615             if (is_str)
616               fprintf(fout, "char     %s[];\n", sym);
617             else
618               fprintf(fout, "uint8_t  %s;\n", sym);
619             break;
620
621           case DXT_WORD:
622             fprintf(fout, "uint16_t %s;\n", sym);
623             break;
624
625           case DXT_DWORD:
626             fprintf(fout, "uint32_t %s;\n", sym);
627             break;
628
629           default:
630             fprintf(fout, "_UNKNOWN %s;\n", sym);
631             break;
632           }
633
634           continue;
635         }
636
637         snprintf(last_sym, sizeof(last_sym), "%s", sym);
638         maybe_func_table = type == DXT_DWORD;
639
640         if (IS_START(sym, "__IMPORT_DESCRIPTOR_")) {
641           rm_labels_lines = 5;
642           maybe_func_table = 0;
643         }
644
645         pp = proto_parse(fhdr, sym, 1);
646         if (pp != NULL) {
647           g_func_sym_pp = NULL;
648
649           // public/global name
650           if (pub_sym_cnt >= pub_sym_alloc) {
651             pub_sym_alloc *= 2;
652             pub_syms = realloc(pub_syms, pub_sym_alloc * sizeof(pub_syms[0]));
653             my_assert_not(pub_syms, NULL);
654           }
655           pub_syms[pub_sym_cnt++] = strdup(sym);
656         }
657
658         len = strlen(sym);
659         fprintf(fout, "%s%s:", no_decorations ? "" : "_", sym);
660
661         len += 2;
662         if (len < 8)
663           fprintf(fout, "\t");
664         if (len < 16)
665           fprintf(fout, "\t");
666         if (len <= 16)
667           fprintf(fout, "  ");
668         else
669           fprintf(fout, " ");
670       }
671       else {
672         if (header_mode)
673           continue;
674
675         fprintf(fout, "\t\t  ");
676       }
677
678       // fill out some unwanted strings with zeroes..
679       if (type == DXT_BYTE && words[w][0] == '\''
680         && is_unwanted_sym(last_sym))
681       {
682         len = 0;
683         for (; w < wordc; w++) {
684           if (words[w][0] == '\'') {
685             p = words[w] + 1;
686             for (; *p && *p != '\''; p++)
687               len++;
688           }
689           else {
690             // assume encoded byte
691             len++;
692           }
693         }
694         fprintf(fout, ".skip %d", len);
695         goto fin;
696       }
697       else if (type == DXT_BYTE
698         && (words[w][0] == '\''
699             || (w + 1 < wordc && words[w + 1][0] == '\'')))
700       {
701         // string; use asciz for most common case
702         if (w == wordc - 2 && IS(words[w + 1], "0")) {
703           fprintf(fout, ".asciz \"");
704           wordc--;
705         }
706         else
707           fprintf(fout, ".ascii \"");
708
709         for (; w < wordc; w++) {
710           if (words[w][0] == '\'') {
711             p = words[w] + 1;
712             p2 = strchr(p, '\'');
713             if (p2 == NULL)
714               aerr("unterminated string? '%s'\n", p);
715             memcpy(word, p, p2 - p);
716             word[p2 - p] = 0;
717             fprintf(fout, "%s", escape_string(word));
718           }
719           else {
720             val = parse_number(words[w], 0);
721             if (val & ~0xff)
722               aerr("bad string trailing byte?\n");
723             // unfortunately \xHH is unusable - gas interprets
724             // things like \x27b as 0x7b, so have to use octal here
725             fprintf(fout, "\\%03lo", val);
726           }
727         }
728         fprintf(fout, "\"");
729         goto fin;
730       }
731
732       if (w == wordc - 2) {
733         if (IS_START(words[w + 1], "dup(")) {
734           cnt = parse_number(words[w], 0);
735           p = words[w + 1] + 4;
736           p2 = strchr(p, ')');
737           if (p2 == NULL)
738             aerr("bad dup?\n");
739           memmove(word, p, p2 - p);
740           word[p2 - p] = 0;
741
742           val = 0;
743           if (!IS(word, "?"))
744             val = parse_number(word, 0);
745
746           fprintf(fout, ".fill 0x%02lx,%d,0x%02lx",
747             cnt, type_size(type), val);
748           goto fin;
749         }
750       }
751
752       if (type == DXT_DWORD && words[w][0] == '\''
753         && words[w][5] == '\'' && strlen(words[w]) == 6)
754       {
755         if (w != wordc - 1)
756           aerr("TODO\n");
757
758         p = words[w];
759         val = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4];
760         fprintf(fout, ".long 0x%lx", val);
761         snprintf(g_comment, sizeof(g_comment), "%s", words[w]);
762         goto fin;
763       }
764
765       if (type >= DXT_DWORD && strchr(words[w], '.'))
766       {
767         if (w != wordc - 1)
768           aerr("TODO\n");
769
770         if (g_arm_mode && type == DXT_TEN) {
771           fprintf(fout, ".fill 10");
772           snprintf(g_comment, sizeof(g_comment), "%s %s",
773             type_name_float(type), words[w]);
774         }
775         else
776           fprintf(fout, "%s %s", type_name_float(type), words[w]);
777         goto fin;
778       }
779
780       first = 1;
781       fprintf(fout, "%s ", type_name(type));
782       for (; w < wordc; w++)
783       {
784         if (!first)
785           fprintf(fout, ", ");
786
787         is_label = is_bss = 0;
788         if (w <= wordc - 2 && IS(words[w], "offset")) {
789           is_label = 1;
790           w++;
791         }
792         else if (IS(words[w], "?")) {
793           is_bss = 1;
794         }
795         else if (type == DXT_DWORD
796                  && !('0' <= words[w][0] && words[w][0] <= '9'))
797         {
798           // assume label
799           is_label = 1;
800         }
801
802         if (is_bss) {
803           fprintf(fout, "0");
804         }
805         else if (is_label) {
806           p = words[w];
807           if (IS_START(p, "loc_") || IS_START(p, "__imp")
808              || strchr(p, '?') || strchr(p, '@')
809              || rm_labels_lines > 0
810              || bsearch(&p, rlist, rlist_cnt, sizeof(rlist[0]),
811                   cmpstringp))
812           {
813             fprintf(fout, "0");
814             snprintf(g_comment, sizeof(g_comment), "%s", p);
815           }
816           else {
817             const char *f_sym = maybe_func_table ? last_sym : NULL;
818
819             pp = check_var(fhdr, f_sym, p, in_export_table);
820             if (pp == NULL) {
821               fprintf(fout, "%s%s",
822                 (no_decorations || p[0] == '_') ? "" : "_", p);
823             }
824             else {
825               if (no_decorations)
826                 fprintf(fout, "%s", pp->name);
827               else
828                 output_decorated_pp(fout, pp);
829             }
830           }
831         }
832         else {
833           val64 = parse_number(words[w], 1);
834           if (val64 < 10)
835             fprintf(fout, "%d", (int)val64);
836           else
837             fprintf(fout, "0x%" PRIx64, val64);
838
839           is_zero_val = val64 == 0;
840         }
841
842         first = 0;
843       }
844
845 fin:
846       if (!is_zero_val)
847         maybe_func_table = 0;
848
849       if (rm_labels_lines > 0)
850         rm_labels_lines--;
851
852       if (g_comment[0] != 0) {
853         fprintf(fout, "\t\t%c %s", comment_char, g_comment);
854         g_comment[0] = 0;
855       }
856       fprintf(fout, "\n");
857     }
858   }
859
860   fprintf(fout, "\n");
861
862   // dump public syms
863   for (i = 0; i < pub_sym_cnt; i++)
864     fprintf(fout, ".global %s%s\n",
865       no_decorations ? "" : "_", pub_syms[i]);
866
867   fclose(fout);
868   fclose(fasm);
869   if (fhdr != NULL)
870     fclose(fhdr);
871
872   return 0;
873 }
874
875 // vim:ts=2:shiftwidth=2:expandtab