fix edx arg-reg corruption in fromasm
[ia32rtools.git] / tools / mkbridge.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 #include "my_assert.h"
6 #include "my_str.h"
7
8 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
9 #define IS(w, y) !strcmp(w, y)
10
11 #include "protoparse.h"
12
13 static int is_x86_reg_saved(const char *reg)
14 {
15         static const char *nosave_regs[] = { "eax", "edx", "ecx" };
16         int nosave = 0;
17         int r;
18
19         for (r = 0; r < ARRAY_SIZE(nosave_regs); r++)
20                 if (strcmp(reg, nosave_regs[r]) == 0)
21                         nosave = 1;
22
23         return !nosave;
24 }
25
26 static void out_toasm_x86(FILE *f, char *sym, struct parsed_proto *pp)
27 {
28         int must_save = 0;
29         int sarg_ofs = 1; // stack offset to args, in DWORDs
30         int args_repushed = 0;
31         int i;
32
33         for (i = 0; i < pp->argc; i++) {
34                 if (pp->arg[i].reg != NULL)
35                         must_save |= is_x86_reg_saved(pp->arg[i].reg);
36         }
37
38         fprintf(f, ".global _%s\n", sym);
39         fprintf(f, "_%s:\n", sym);
40
41         if (pp->argc_reg == 0 && !pp->is_stdcall) {
42                 fprintf(f, "\tjmp %s\n\n", sym);
43                 return;
44         }
45
46         if (pp->argc_stack == 0 && !must_save && !pp->is_stdcall) {
47                 // load arg regs
48                 for (i = 0; i < pp->argc; i++) {
49                         fprintf(f, "\tmovl %d(%%esp), %%%s\n",
50                                 (i + sarg_ofs) * 4, pp->arg[i].reg);
51                 }
52                 fprintf(f, "\tjmp %s\n\n", sym);
53                 return;
54         }
55
56         // save the regs
57         for (i = 0; i < pp->argc; i++) {
58                 if (pp->arg[i].reg != NULL && is_x86_reg_saved(pp->arg[i].reg)) {
59                         fprintf(f, "\tpushl %%%s\n", pp->arg[i].reg);
60                         sarg_ofs++;
61                 }
62         }
63
64         // reconstruct arg stack
65         for (i = pp->argc - 1; i >= 0; i--) {
66                 if (pp->arg[i].reg == NULL) {
67                         fprintf(f, "\tmovl %d(%%esp), %%eax\n",
68                                 (i + sarg_ofs) * 4);
69                         fprintf(f, "\tpushl %%eax\n");
70                         sarg_ofs++;
71                         args_repushed++;
72                 }
73         }
74         my_assert(args_repushed, pp->argc_stack);
75
76         // load arg regs
77         for (i = 0; i < pp->argc; i++) {
78                 if (pp->arg[i].reg != NULL) {
79                         fprintf(f, "\tmovl %d(%%esp), %%%s\n",
80                                 (i + sarg_ofs) * 4, pp->arg[i].reg);
81                 }
82         }
83
84         fprintf(f, "\n\t# %s\n", pp->is_stdcall ? "__stdcall" : "__cdecl");
85         fprintf(f, "\tcall %s\n\n", sym);
86
87         if (args_repushed && !pp->is_stdcall)
88                 fprintf(f, "\tadd $%d,%%esp\n", args_repushed * 4);
89
90         // restore regs
91         for (i = pp->argc - 1; i >= 0; i--) {
92                 if (pp->arg[i].reg != NULL && is_x86_reg_saved(pp->arg[i].reg))
93                         fprintf(f, "\tpopl %%%s\n", pp->arg[i].reg);
94         }
95
96         fprintf(f, "\tret\n\n");
97 }
98
99 static void out_fromasm_x86(FILE *f, char *sym, struct parsed_proto *pp)
100 {
101         int sarg_ofs = 1; // stack offset to args, in DWORDs
102         int stack_args;
103         int i;
104
105         fprintf(f, "# %s\n", pp->is_stdcall ? "__stdcall" : "__cdecl");
106         fprintf(f, ".global %s\n", sym);
107         fprintf(f, "%s:\n", sym);
108
109         if (pp->argc_reg == 0 && !pp->is_stdcall) {
110                 fprintf(f, "\tjmp _%s\n\n", sym);
111                 return;
112         }
113
114         fprintf(f, "\tpushl %%edx\n"); // just in case..
115         sarg_ofs++;
116
117         // construct arg stack
118         stack_args = pp->argc_stack;
119         for (i = pp->argc - 1; i >= 0; i--) {
120                 if (pp->arg[i].reg == NULL) {
121                         fprintf(f, "\tmovl %d(%%esp), %%edx\n",
122                                 (sarg_ofs + stack_args - 1) * 4);
123                         fprintf(f, "\tpushl %%edx\n");
124                         stack_args--;
125                 }
126                 else {
127                         if (IS(pp->arg[i].reg, "edx"))
128                                 // must reload original edx
129                                 fprintf(f, "\tmovl %d(%%esp), %%edx\n",
130                                         (sarg_ofs - 2) * 4);
131
132                         fprintf(f, "\tpushl %%%s\n", pp->arg[i].reg);
133                 }
134                 sarg_ofs++;
135         }
136
137         // no worries about calling conventions - always __cdecl
138         fprintf(f, "\n\tcall _%s\n\n", sym);
139
140         if (sarg_ofs > 2)
141                 fprintf(f, "\tadd $%d,%%esp\n", (sarg_ofs - 2) * 4);
142
143         fprintf(f, "\tpopl %%edx\n");
144
145         if (pp->is_stdcall && pp->argc_stack)
146                 fprintf(f, "\tret $%d\n\n", pp->argc_stack * 4);
147         else
148                 fprintf(f, "\tret\n\n");
149 }
150
151 int main(int argc, char *argv[])
152 {
153         FILE *fout, *fsyms_to, *fsyms_from, *fhdr;
154         struct parsed_proto pp;
155         char line[256];
156         char sym[256];
157         int ret;
158
159         if (argc != 5) {
160                 printf("usage:\n%s <bridge.s> <toasm_symf> <fromasm_symf> <hdrf>\n",
161                         argv[0]);
162                 return 1;
163         }
164
165         hdrfn = argv[4];
166         fhdr = fopen(hdrfn, "r");
167         my_assert_not(fhdr, NULL);
168
169         fsyms_from = fopen(argv[3], "r");
170         my_assert_not(fsyms_from, NULL);
171
172         fsyms_to = fopen(argv[2], "r");
173         my_assert_not(fsyms_to, NULL);
174
175         fout = fopen(argv[1], "w");
176         my_assert_not(fout, NULL);
177
178         fprintf(fout, ".text\n\n");
179         fprintf(fout, "# to asm\n\n");
180
181         while (fgets(line, sizeof(line), fsyms_to))
182         {
183                 next_word(sym, sizeof(sym), line);
184                 if (sym[0] == 0 || sym[0] == ';' || sym[0] == '#')
185                         continue;
186
187                 ret = proto_parse(fhdr, sym, &pp);
188                 if (ret)
189                         goto out;
190
191                 out_toasm_x86(fout, sym, &pp);
192                 proto_release(&pp);
193         }
194
195         fprintf(fout, "# from asm\n\n");
196
197         while (fgets(line, sizeof(line), fsyms_from))
198         {
199                 next_word(sym, sizeof(sym), line);
200                 if (sym[0] == 0 || sym[0] == ';' || sym[0] == '#')
201                         continue;
202
203                 ret = proto_parse(fhdr, sym, &pp);
204                 if (ret)
205                         goto out;
206
207                 out_fromasm_x86(fout, sym, &pp);
208                 proto_release(&pp);
209         }
210
211         ret = 0;
212 out:
213         fclose(fout);
214         fclose(fsyms_to);
215         fclose(fsyms_from);
216         fclose(fhdr);
217         if (ret)
218                 remove(argv[1]);
219
220         return ret;
221 }