cvt_data: zero out import tables
[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_cmp_func(pp, pp_sym)) {
310     if (pp_sym->argc_stack == 0 && pp_sym->is_fastcall
311         && pp->argc_stack == 0
312         && (pp->is_fastcall || pp->argc_reg == 0)
313         && pp_sym->argc_reg > pp->argc_reg)
314       ; /* fascall compatible func doesn't use all args -> ok */
315     else {
316       pp_print(fp_sym, sizeof(fp_sym), pp_sym);
317       anote("var: %s\n", fp_var);
318       anote("sym: %s\n", fp_sym);
319       awarn("^ mismatch\n");
320     }
321   }
322
323   return pp;
324 }
325
326 static void output_decorated_pp(FILE *fout,
327   const struct parsed_proto *pp)
328 {
329   if (pp->name[0] != '_')
330     fprintf(fout, pp->is_fastcall ? "@" : "_");
331   fprintf(fout, "%s", pp->name);
332   if (pp->is_stdcall && pp->argc > 0)
333     fprintf(fout, "@%d", pp->argc * 4);
334 }
335
336 static int align_value(int src_val)
337 {
338   if (src_val <= 0) {
339     awarn("bad align: %d\n", src_val);
340     src_val = 1;
341   }
342   if (!g_arm_mode)
343     return src_val;
344
345   return __builtin_ffs(src_val) - 1;
346 }
347
348 static int cmpstringp(const void *p1, const void *p2)
349 {
350   return strcmp(*(char * const *)p1, *(char * const *)p2);
351 }
352
353 /* XXX: maybe move to external file? */
354 static const char *unwanted_syms[] = {
355   "aRuntimeError",
356   "aTlossError",
357   "aSingError",
358   "aDomainError",
359   "aR6029ThisAppli",
360   "aR6028UnableToI",
361   "aR6027NotEnough",
362   "aR6026NotEnough",
363   "aR6025PureVirtu",
364   "aR6024NotEnough",
365   "aR6019UnableToO",
366   "aR6018Unexpecte",
367   "aR6017Unexpecte",
368   "aR6016NotEnough",
369   "aAbnormalProgra",
370   "aR6009NotEnough",
371   "aR6008NotEnough",
372   "aR6002FloatingP",
373   "aMicrosoftVisua",
374   "aRuntimeErrorPr",
375   "aThisApplicatio",
376   "aMicrosoftFindF",
377   "aMicrosoftOffic",
378 };
379
380 static int is_unwanted_sym(const char *sym)
381 {
382   return bsearch(&sym, unwanted_syms, ARRAY_SIZE(unwanted_syms),
383     sizeof(unwanted_syms[0]), cmpstringp) != NULL;
384 }
385
386 int main(int argc, char *argv[])
387 {
388   FILE *fout, *fasm, *fhdr = NULL, *frlist;
389   const struct parsed_proto *pp;
390   int no_decorations = 0;
391   int in_export_table = 0;
392   int rm_labels_lines = 0;
393   char comment_char = '#';
394   char words[20][256];
395   char word[256];
396   char line[256];
397   char last_sym[32];
398   unsigned long val;
399   unsigned long cnt;
400   uint64_t val64;
401   const char *sym;
402   enum dx_type type;
403   char **pub_syms;
404   int pub_sym_cnt = 0;
405   int pub_sym_alloc;
406   char **rlist;
407   int rlist_cnt = 0;
408   int rlist_alloc;
409   int header_mode = 0;
410   int is_ro = 0;
411   int is_label;
412   int is_bss;
413   int wordc;
414   int first;
415   int arg_out;
416   int arg = 1;
417   int len;
418   int w, i;
419   char *p;
420   char *p2;
421
422   if (argc < 4) {
423     // -nd: no symbol decorations
424     printf("usage:\n%s [-nd] [-i] [-a] <.s> <.asm> <hdrf> [rlist]*\n"
425            "%s -hdr <.h> <.asm>\n",
426       argv[0], argv[0]);
427     return 1;
428   }
429
430   for (arg = 1; arg < argc; arg++) {
431     if (IS(argv[arg], "-nd"))
432       no_decorations = 1;
433     else if (IS(argv[arg], "-i"))
434       g_cconv_novalidate = 1;
435     else if (IS(argv[arg], "-a")) {
436       comment_char = '@';
437       g_arm_mode = 1;
438     }
439     else if (IS(argv[arg], "-hdr"))
440       header_mode = 1;
441     else
442       break;
443   }
444
445   arg_out = arg++;
446
447   asmfn = argv[arg++];
448   fasm = fopen(asmfn, "r");
449   my_assert_not(fasm, NULL);
450
451   if (!header_mode) {
452     hdrfn = argv[arg++];
453     fhdr = fopen(hdrfn, "r");
454     my_assert_not(fhdr, NULL);
455   }
456
457   fout = fopen(argv[arg_out], "w");
458   my_assert_not(fout, NULL);
459
460   pub_sym_alloc = 64;
461   pub_syms = malloc(pub_sym_alloc * sizeof(pub_syms[0]));
462   my_assert_not(pub_syms, NULL);
463
464   rlist_alloc = 64;
465   rlist = malloc(rlist_alloc * sizeof(rlist[0]));
466   my_assert_not(rlist, NULL);
467
468   for (; arg < argc; arg++) {
469     frlist = fopen(argv[arg], "r");
470     my_assert_not(frlist, NULL);
471
472     while (my_fgets(line, sizeof(line), frlist)) {
473       p = sskip(line);
474       if (*p == 0 || *p == ';' || *p == '#')
475         continue;
476
477       p = next_word(words[0], sizeof(words[0]), p);
478       if (words[0][0] == 0)
479         continue;
480
481       if (rlist_cnt >= rlist_alloc) {
482         rlist_alloc = rlist_alloc * 2 + 64;
483         rlist = realloc(rlist, rlist_alloc * sizeof(rlist[0]));
484         my_assert_not(rlist, NULL);
485       }
486       rlist[rlist_cnt++] = strdup(words[0]);
487     }
488
489     fclose(frlist);
490     frlist = NULL;
491   }
492
493   if (rlist_cnt > 0)
494     qsort(rlist, rlist_cnt, sizeof(rlist[0]), cmpstringp);
495
496   qsort(unwanted_syms, ARRAY_SIZE(unwanted_syms),
497     sizeof(unwanted_syms[0]), cmpstringp);
498
499   last_sym[0] = 0;
500
501   while (1) {
502     next_section(fasm, line);
503     if (feof(fasm))
504       break;
505     if (IS(line + 1, "text"))
506       continue;
507
508     if (IS(line + 1, "rdata")) {
509       is_ro = 1;
510       if (!header_mode)
511         fprintf(fout, "\n.section .rodata\n");
512     }
513     else if (IS(line + 1, "data")) {
514       is_ro = 0;
515       if (!header_mode)
516         fprintf(fout, "\n.data\n");
517     }
518     else
519       aerr("unhandled section: '%s'\n", line);
520
521     if (!header_mode)
522       fprintf(fout, ".align %d\n", align_value(4));
523
524     while (my_fgets(line, sizeof(line), fasm))
525     {
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           g_func_sym_pp = NULL;
562       }
563
564       if (wordc == 2 && IS(words[1], "ends"))
565         break;
566       if (wordc <= 2 && IS(words[0], "end"))
567         break;
568       if (wordc < 2)
569         aerr("unhandled: '%s'\n", words[0]);
570
571       // don't cares
572       if (IS(words[0], "assume"))
573         continue;
574
575       if (IS(words[0], "align")) {
576         if (header_mode)
577           continue;
578
579         val = parse_number(words[1], 0);
580         fprintf(fout, "\t\t  .align %d", align_value(val));
581         goto fin;
582       }
583
584       if (IS(words[0], "public")) {
585         // skip, sym should appear in header anyway
586         continue;
587       }
588
589       w = 1;
590       type = parse_dx_directive(words[0]);
591       if (type == DXT_UNSPEC) {
592         type = parse_dx_directive(words[1]);
593         sym = words[0];
594         w = 2;
595       }
596       if (type == DXT_UNSPEC)
597         aerr("unhandled decl: '%s %s'\n", words[0], words[1]);
598
599       if (sym != NULL)
600       {
601         if (header_mode) {
602           int is_str = 0;
603
604           fprintf(fout, "extern ");
605           if (is_ro)
606             fprintf(fout, "const ");
607
608           switch (type) {
609           case DXT_BYTE:
610             for (i = w; i < wordc; i++)
611               if (words[i][0] == '\'')
612                 is_str = 1;
613             if (is_str)
614               fprintf(fout, "char     %s[];\n", sym);
615             else
616               fprintf(fout, "uint8_t  %s;\n", sym);
617             break;
618
619           case DXT_WORD:
620             fprintf(fout, "uint16_t %s;\n", sym);
621             break;
622
623           case DXT_DWORD:
624             fprintf(fout, "uint32_t %s;\n", sym);
625             break;
626
627           default:
628             fprintf(fout, "_UNKNOWN %s;\n", sym);
629             break;
630           }
631
632           continue;
633         }
634
635         snprintf(last_sym, sizeof(last_sym), "%s", sym);
636         if (IS_START(sym, "__IMPORT_DESCRIPTOR_"))
637           rm_labels_lines = 5;
638
639         pp = proto_parse(fhdr, sym, 1);
640         if (pp != NULL) {
641           g_func_sym_pp = NULL;
642
643           // public/global name
644           if (pub_sym_cnt >= pub_sym_alloc) {
645             pub_sym_alloc *= 2;
646             pub_syms = realloc(pub_syms, pub_sym_alloc * sizeof(pub_syms[0]));
647             my_assert_not(pub_syms, NULL);
648           }
649           pub_syms[pub_sym_cnt++] = strdup(sym);
650         }
651
652         len = strlen(sym);
653         fprintf(fout, "%s%s:", no_decorations ? "" : "_", sym);
654
655         len += 2;
656         if (len < 8)
657           fprintf(fout, "\t");
658         if (len < 16)
659           fprintf(fout, "\t");
660         if (len <= 16)
661           fprintf(fout, "  ");
662         else
663           fprintf(fout, " ");
664       }
665       else {
666         if (header_mode)
667           continue;
668
669         fprintf(fout, "\t\t  ");
670       }
671
672       // fill out some unwanted strings with zeroes..
673       if (type == DXT_BYTE && words[w][0] == '\''
674         && is_unwanted_sym(last_sym))
675       {
676         len = 0;
677         for (; w < wordc; w++) {
678           if (words[w][0] == '\'') {
679             p = words[w] + 1;
680             for (; *p && *p != '\''; p++)
681               len++;
682           }
683           else {
684             // assume encoded byte
685             len++;
686           }
687         }
688         fprintf(fout, ".skip %d", len);
689         goto fin;
690       }
691       else if (type == DXT_BYTE
692         && (words[w][0] == '\''
693             || (w + 1 < wordc && words[w + 1][0] == '\'')))
694       {
695         // string; use asciz for most common case
696         if (w == wordc - 2 && IS(words[w + 1], "0")) {
697           fprintf(fout, ".asciz \"");
698           wordc--;
699         }
700         else
701           fprintf(fout, ".ascii \"");
702
703         for (; w < wordc; w++) {
704           if (words[w][0] == '\'') {
705             p = words[w] + 1;
706             p2 = strchr(p, '\'');
707             if (p2 == NULL)
708               aerr("unterminated string? '%s'\n", p);
709             memcpy(word, p, p2 - p);
710             word[p2 - p] = 0;
711             fprintf(fout, "%s", escape_string(word));
712           }
713           else {
714             val = parse_number(words[w], 0);
715             if (val & ~0xff)
716               aerr("bad string trailing byte?\n");
717             // unfortunately \xHH is unusable - gas interprets
718             // things like \x27b as 0x7b, so have to use octal here
719             fprintf(fout, "\\%03lo", val);
720           }
721         }
722         fprintf(fout, "\"");
723         goto fin;
724       }
725
726       if (w == wordc - 2) {
727         if (IS_START(words[w + 1], "dup(")) {
728           cnt = parse_number(words[w], 0);
729           p = words[w + 1] + 4;
730           p2 = strchr(p, ')');
731           if (p2 == NULL)
732             aerr("bad dup?\n");
733           memmove(word, p, p2 - p);
734           word[p2 - p] = 0;
735
736           val = 0;
737           if (!IS(word, "?"))
738             val = parse_number(word, 0);
739
740           fprintf(fout, ".fill 0x%02lx,%d,0x%02lx",
741             cnt, type_size(type), val);
742           goto fin;
743         }
744       }
745
746       if (type == DXT_DWORD && words[w][0] == '\''
747         && words[w][5] == '\'' && strlen(words[w]) == 6)
748       {
749         if (w != wordc - 1)
750           aerr("TODO\n");
751
752         p = words[w];
753         val = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4];
754         fprintf(fout, ".long 0x%lx", val);
755         snprintf(g_comment, sizeof(g_comment), "%s", words[w]);
756         goto fin;
757       }
758
759       if (type >= DXT_DWORD && strchr(words[w], '.'))
760       {
761         if (w != wordc - 1)
762           aerr("TODO\n");
763
764         if (g_arm_mode && type == DXT_TEN) {
765           fprintf(fout, ".fill 10");
766           snprintf(g_comment, sizeof(g_comment), "%s %s",
767             type_name_float(type), words[w]);
768         }
769         else
770           fprintf(fout, "%s %s", type_name_float(type), words[w]);
771         goto fin;
772       }
773
774       first = 1;
775       fprintf(fout, "%s ", type_name(type));
776       for (; w < wordc; w++)
777       {
778         if (!first)
779           fprintf(fout, ", ");
780
781         is_label = is_bss = 0;
782         if (w <= wordc - 2 && IS(words[w], "offset")) {
783           is_label = 1;
784           w++;
785         }
786         else if (IS(words[w], "?")) {
787           is_bss = 1;
788         }
789         else if (type == DXT_DWORD
790                  && !('0' <= words[w][0] && words[w][0] <= '9'))
791         {
792           // assume label
793           is_label = 1;
794         }
795
796         if (is_bss) {
797           fprintf(fout, "0");
798         }
799         else if (is_label) {
800           p = words[w];
801           if (IS_START(p, "loc_") || IS_START(p, "__imp")
802              || strchr(p, '?') || strchr(p, '@')
803              || rm_labels_lines > 0
804              || bsearch(&p, rlist, rlist_cnt, sizeof(rlist[0]),
805                   cmpstringp))
806           {
807             fprintf(fout, "0");
808             snprintf(g_comment, sizeof(g_comment), "%s", p);
809           }
810           else {
811             pp = check_var(fhdr, sym, p, in_export_table);
812             if (pp == NULL) {
813               fprintf(fout, "%s%s",
814                 (no_decorations || p[0] == '_') ? "" : "_", p);
815             }
816             else {
817               if (no_decorations)
818                 fprintf(fout, "%s", pp->name);
819               else
820                 output_decorated_pp(fout, pp);
821             }
822           }
823         }
824         else {
825           val64 = parse_number(words[w], 1);
826           if (val64 < 10)
827             fprintf(fout, "%d", (int)val64);
828           else
829             fprintf(fout, "0x%" PRIx64, val64);
830         }
831
832         first = 0;
833       }
834
835 fin:
836       if (rm_labels_lines > 0)
837         rm_labels_lines--;
838
839       if (g_comment[0] != 0) {
840         fprintf(fout, "\t\t%c %s", comment_char, g_comment);
841         g_comment[0] = 0;
842       }
843       fprintf(fout, "\n");
844     }
845   }
846
847   fprintf(fout, "\n");
848
849   // dump public syms
850   for (i = 0; i < pub_sym_cnt; i++)
851     fprintf(fout, ".global %s%s\n",
852       no_decorations ? "" : "_", pub_syms[i]);
853
854   fclose(fout);
855   fclose(fasm);
856   if (fhdr != NULL)
857     fclose(fhdr);
858
859   return 0;
860 }
861
862 // vim:ts=2:shiftwidth=2:expandtab