translate: fixes for pop scan
[ia32rtools.git] / tools / cvt_data.c
... / ...
CommitLineData
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
22static const char *asmfn;
23static int asmln;
24
25static const struct parsed_proto *g_func_sym_pp;
26static char g_comment[256];
27static int g_warn_cnt;
28static int g_cconv_novalidate;
29static int g_arm_mode;
30
31// note: must be in ascending order
32enum 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
58static 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
80static 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
120static 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
136static 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
155static 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
170static 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
189static 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
213static 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
241static 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
277check_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
326static 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
336static 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
348static 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? */
354static 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
380static 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
386int 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
835fin:
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