minor fixes
[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_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
319static 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
329static 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
341static 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? */
347static 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
373static 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
379int 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
845fin:
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