translate: tune float i/o, indirect tailcalls
[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 in_export_table = 0;
385 int rm_labels_lines = 0;
386 char comment_char = '#';
387 char words[20][256];
388 char word[256];
389 char line[256];
390 char last_sym[32];
391 unsigned long val;
392 unsigned long cnt;
393 uint64_t val64;
394 const char *sym;
395 enum dx_type type;
396 char **pub_syms;
397 int pub_sym_cnt = 0;
398 int pub_sym_alloc;
399 char **rlist;
400 int rlist_cnt = 0;
401 int rlist_alloc;
402 int header_mode = 0;
403 int is_ro = 0;
404 int is_label;
405 int is_bss;
406 int wordc;
407 int first;
408 int arg_out;
409 int arg = 1;
410 int len;
411 int w, i;
412 char *p;
413 char *p2;
414
415 if (argc < 4) {
416 // -nd: no symbol decorations
417 printf("usage:\n%s [-nd] [-i] [-a] <.s> <.asm> <hdrf> [rlist]*\n"
418 "%s -hdr <.h> <.asm>\n",
419 argv[0], argv[0]);
420 return 1;
421 }
422
423 for (arg = 1; arg < argc; arg++) {
424 if (IS(argv[arg], "-nd"))
425 no_decorations = 1;
426 else if (IS(argv[arg], "-i"))
427 g_cconv_novalidate = 1;
428 else if (IS(argv[arg], "-a")) {
429 comment_char = '@';
430 g_arm_mode = 1;
431 }
432 else if (IS(argv[arg], "-hdr"))
433 header_mode = 1;
434 else
435 break;
436 }
437
438 arg_out = arg++;
439
440 asmfn = argv[arg++];
441 fasm = fopen(asmfn, "r");
442 my_assert_not(fasm, NULL);
443
444 if (!header_mode) {
445 hdrfn = argv[arg++];
446 fhdr = fopen(hdrfn, "r");
447 my_assert_not(fhdr, NULL);
448 }
449
450 fout = fopen(argv[arg_out], "w");
451 my_assert_not(fout, NULL);
452
453 pub_sym_alloc = 64;
454 pub_syms = malloc(pub_sym_alloc * sizeof(pub_syms[0]));
455 my_assert_not(pub_syms, NULL);
456
457 rlist_alloc = 64;
458 rlist = malloc(rlist_alloc * sizeof(rlist[0]));
459 my_assert_not(rlist, NULL);
460
461 for (; arg < argc; arg++) {
462 frlist = fopen(argv[arg], "r");
463 my_assert_not(frlist, NULL);
464
465 while (my_fgets(line, sizeof(line), frlist)) {
466 p = sskip(line);
467 if (*p == 0 || *p == ';' || *p == '#')
468 continue;
469
470 p = next_word(words[0], sizeof(words[0]), p);
471 if (words[0][0] == 0)
472 continue;
473
474 if (rlist_cnt >= rlist_alloc) {
475 rlist_alloc = rlist_alloc * 2 + 64;
476 rlist = realloc(rlist, rlist_alloc * sizeof(rlist[0]));
477 my_assert_not(rlist, NULL);
478 }
479 rlist[rlist_cnt++] = strdup(words[0]);
480 }
481
482 fclose(frlist);
483 frlist = NULL;
484 }
485
486 if (rlist_cnt > 0)
487 qsort(rlist, rlist_cnt, sizeof(rlist[0]), cmpstringp);
488
489 qsort(unwanted_syms, ARRAY_SIZE(unwanted_syms),
490 sizeof(unwanted_syms[0]), cmpstringp);
491
492 last_sym[0] = 0;
493
494 while (1) {
495 next_section(fasm, line);
496 if (feof(fasm))
497 break;
498 if (IS(line + 1, "text"))
499 continue;
500
501 if (IS(line + 1, "rdata")) {
502 is_ro = 1;
503 if (!header_mode)
504 fprintf(fout, "\n.section .rodata\n");
505 }
506 else if (IS(line + 1, "data")) {
507 is_ro = 0;
508 if (!header_mode)
509 fprintf(fout, "\n.data\n");
510 }
511 else
512 aerr("unhandled section: '%s'\n", line);
513
514 if (!header_mode)
515 fprintf(fout, ".align %d\n", align_value(4));
516
517 while (my_fgets(line, sizeof(line), fasm))
518 {
519 sym = NULL;
520 asmln++;
521
522 p = sskip(line);
523 if (*p == 0)
524 continue;
525
526 if (*p == ';') {
527 if (IS_START(p, ";org") && sscanf(p + 5, "%Xh", &i) == 1) {
528 // ;org is only seen at section start, so assume . addr 0
529 i &= 0xfff;
530 if (i != 0 && !header_mode)
531 fprintf(fout, "\t\t .skip 0x%x\n", i);
532 }
533 else if (IS_START(p, "; Export Address"))
534 in_export_table = 1;
535 else if (IS_START(p, "; Export"))
536 in_export_table = 0;
537 continue;
538 }
539
540 for (wordc = 0; wordc < ARRAY_SIZE(words); wordc++) {
541 p = sskip(next_word_s(words[wordc], sizeof(words[0]), p));
542 if (*p == 0 || *p == ';') {
543 wordc++;
544 break;
545 }
546 if (*p == ',') {
547 p = sskip(p + 1);
548 }
549 }
550
551 if (*p == ';') {
552 p = sskip(p + 1);
553 if (IS_START(p, "sctclrtype"))
554 g_func_sym_pp = NULL;
555 }
556
557 if (wordc == 2 && IS(words[1], "ends"))
558 break;
559 if (wordc <= 2 && IS(words[0], "end"))
560 break;
561 if (wordc < 2)
562 aerr("unhandled: '%s'\n", words[0]);
563
564 // don't cares
565 if (IS(words[0], "assume"))
566 continue;
567
568 if (IS(words[0], "align")) {
569 if (header_mode)
570 continue;
571
572 val = parse_number(words[1], 0);
573 fprintf(fout, "\t\t .align %d", align_value(val));
574 goto fin;
575 }
576
577 if (IS(words[0], "public")) {
578 // skip, sym should appear in header anyway
579 continue;
580 }
581
582 w = 1;
583 type = parse_dx_directive(words[0]);
584 if (type == DXT_UNSPEC) {
585 type = parse_dx_directive(words[1]);
586 sym = words[0];
587 w = 2;
588 }
589 if (type == DXT_UNSPEC)
590 aerr("unhandled decl: '%s %s'\n", words[0], words[1]);
591
592 if (sym != NULL)
593 {
594 if (header_mode) {
595 int is_str = 0;
596
597 fprintf(fout, "extern ");
598 if (is_ro)
599 fprintf(fout, "const ");
600
601 switch (type) {
602 case DXT_BYTE:
603 for (i = w; i < wordc; i++)
604 if (words[i][0] == '\'')
605 is_str = 1;
606 if (is_str)
607 fprintf(fout, "char %s[];\n", sym);
608 else
609 fprintf(fout, "uint8_t %s;\n", sym);
610 break;
611
612 case DXT_WORD:
613 fprintf(fout, "uint16_t %s;\n", sym);
614 break;
615
616 case DXT_DWORD:
617 fprintf(fout, "uint32_t %s;\n", sym);
618 break;
619
620 default:
621 fprintf(fout, "_UNKNOWN %s;\n", sym);
622 break;
623 }
624
625 continue;
626 }
627
628 snprintf(last_sym, sizeof(last_sym), "%s", sym);
629 if (IS_START(sym, "__IMPORT_DESCRIPTOR_"))
630 rm_labels_lines = 5;
631
632 pp = proto_parse(fhdr, sym, 1);
633 if (pp != NULL) {
634 g_func_sym_pp = NULL;
635
636 // public/global name
637 if (pub_sym_cnt >= pub_sym_alloc) {
638 pub_sym_alloc *= 2;
639 pub_syms = realloc(pub_syms, pub_sym_alloc * sizeof(pub_syms[0]));
640 my_assert_not(pub_syms, NULL);
641 }
642 pub_syms[pub_sym_cnt++] = strdup(sym);
643 }
644
645 len = strlen(sym);
646 fprintf(fout, "%s%s:", no_decorations ? "" : "_", sym);
647
648 len += 2;
649 if (len < 8)
650 fprintf(fout, "\t");
651 if (len < 16)
652 fprintf(fout, "\t");
653 if (len <= 16)
654 fprintf(fout, " ");
655 else
656 fprintf(fout, " ");
657 }
658 else {
659 if (header_mode)
660 continue;
661
662 fprintf(fout, "\t\t ");
663 }
664
665 // fill out some unwanted strings with zeroes..
666 if (type == DXT_BYTE && words[w][0] == '\''
667 && is_unwanted_sym(last_sym))
668 {
669 len = 0;
670 for (; w < wordc; w++) {
671 if (words[w][0] == '\'') {
672 p = words[w] + 1;
673 for (; *p && *p != '\''; p++)
674 len++;
675 }
676 else {
677 // assume encoded byte
678 len++;
679 }
680 }
681 fprintf(fout, ".skip %d", len);
682 goto fin;
683 }
684 else if (type == DXT_BYTE
685 && (words[w][0] == '\''
686 || (w + 1 < wordc && words[w + 1][0] == '\'')))
687 {
688 // string; use asciz for most common case
689 if (w == wordc - 2 && IS(words[w + 1], "0")) {
690 fprintf(fout, ".asciz \"");
691 wordc--;
692 }
693 else
694 fprintf(fout, ".ascii \"");
695
696 for (; w < wordc; w++) {
697 if (words[w][0] == '\'') {
698 p = words[w] + 1;
699 p2 = strchr(p, '\'');
700 if (p2 == NULL)
701 aerr("unterminated string? '%s'\n", p);
702 memcpy(word, p, p2 - p);
703 word[p2 - p] = 0;
704 fprintf(fout, "%s", escape_string(word));
705 }
706 else {
707 val = parse_number(words[w], 0);
708 if (val & ~0xff)
709 aerr("bad string trailing byte?\n");
710 // unfortunately \xHH is unusable - gas interprets
711 // things like \x27b as 0x7b, so have to use octal here
712 fprintf(fout, "\\%03lo", val);
713 }
714 }
715 fprintf(fout, "\"");
716 goto fin;
717 }
718
719 if (w == wordc - 2) {
720 if (IS_START(words[w + 1], "dup(")) {
721 cnt = parse_number(words[w], 0);
722 p = words[w + 1] + 4;
723 p2 = strchr(p, ')');
724 if (p2 == NULL)
725 aerr("bad dup?\n");
726 memmove(word, p, p2 - p);
727 word[p2 - p] = 0;
728
729 val = 0;
730 if (!IS(word, "?"))
731 val = parse_number(word, 0);
732
733 fprintf(fout, ".fill 0x%02lx,%d,0x%02lx",
734 cnt, type_size(type), val);
735 goto fin;
736 }
737 }
738
739 if (type == DXT_DWORD && words[w][0] == '\''
740 && words[w][5] == '\'' && strlen(words[w]) == 6)
741 {
742 if (w != wordc - 1)
743 aerr("TODO\n");
744
745 p = words[w];
746 val = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4];
747 fprintf(fout, ".long 0x%lx", val);
748 snprintf(g_comment, sizeof(g_comment), "%s", words[w]);
749 goto fin;
750 }
751
752 if (type >= DXT_DWORD && strchr(words[w], '.'))
753 {
754 if (w != wordc - 1)
755 aerr("TODO\n");
756
757 if (g_arm_mode && type == DXT_TEN) {
758 fprintf(fout, ".fill 10");
759 snprintf(g_comment, sizeof(g_comment), "%s %s",
760 type_name_float(type), words[w]);
761 }
762 else
763 fprintf(fout, "%s %s", type_name_float(type), words[w]);
764 goto fin;
765 }
766
767 first = 1;
768 fprintf(fout, "%s ", type_name(type));
769 for (; w < wordc; w++)
770 {
771 if (!first)
772 fprintf(fout, ", ");
773
774 is_label = is_bss = 0;
775 if (w <= wordc - 2 && IS(words[w], "offset")) {
776 is_label = 1;
777 w++;
778 }
779 else if (IS(words[w], "?")) {
780 is_bss = 1;
781 }
782 else if (type == DXT_DWORD
783 && !('0' <= words[w][0] && words[w][0] <= '9'))
784 {
785 // assume label
786 is_label = 1;
787 }
788
789 if (is_bss) {
790 fprintf(fout, "0");
791 }
792 else if (is_label) {
793 p = words[w];
794 if (IS_START(p, "loc_") || IS_START(p, "__imp")
795 || strchr(p, '?') || strchr(p, '@')
796 || rm_labels_lines > 0
797 || bsearch(&p, rlist, rlist_cnt, sizeof(rlist[0]),
798 cmpstringp))
799 {
800 fprintf(fout, "0");
801 snprintf(g_comment, sizeof(g_comment), "%s", p);
802 }
803 else {
804 pp = check_var(fhdr, sym, p, in_export_table);
805 if (pp == NULL) {
806 fprintf(fout, "%s%s",
807 (no_decorations || p[0] == '_') ? "" : "_", p);
808 }
809 else {
810 if (no_decorations)
811 fprintf(fout, "%s", pp->name);
812 else
813 output_decorated_pp(fout, pp);
814 }
815 }
816 }
817 else {
818 val64 = parse_number(words[w], 1);
819 if (val64 < 10)
820 fprintf(fout, "%d", (int)val64);
821 else
822 fprintf(fout, "0x%" PRIx64, val64);
823 }
824
825 first = 0;
826 }
827
828fin:
829 if (rm_labels_lines > 0)
830 rm_labels_lines--;
831
832 if (g_comment[0] != 0) {
833 fprintf(fout, "\t\t%c %s", comment_char, g_comment);
834 g_comment[0] = 0;
835 }
836 fprintf(fout, "\n");
837 }
838 }
839
840 fprintf(fout, "\n");
841
842 // dump public syms
843 for (i = 0; i < pub_sym_cnt; i++)
844 fprintf(fout, ".global %s%s\n",
845 no_decorations ? "" : "_", pub_syms[i]);
846
847 fclose(fout);
848 fclose(fasm);
849 if (fhdr != NULL)
850 fclose(fhdr);
851
852 return 0;
853}
854
855// vim:ts=2:shiftwidth=2:expandtab