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