adding disasm code, lots of breakage
[ia32rtools.git] / tools / mkbridge.c
CommitLineData
57e4efe9 1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4
5#include "my_assert.h"
6#include "my_str.h"
7
232aca37 8#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
3e52f54c 9#define IS(w, y) !strcmp(w, y)
232aca37 10
57e4efe9 11static int find_protostr(char *dst, size_t dlen, FILE *fhdr,
12 const char *sym, int *pline)
13{
14 int line = 0;
15 char *p;
16
232aca37 17 rewind(fhdr);
18
57e4efe9 19 while (fgets(dst, dlen, fhdr))
20 {
21 line++;
22 if (strstr(dst, sym) != NULL)
23 break;
24 }
25 *pline = line;
26
27 if (feof(fhdr))
28 return -1;
29
30 p = dst + strlen(dst);
31 for (p--; p > dst && my_isblank(*p); --p)
32 *p = 0;
33
34 return 0;
35}
36
37static int get_regparm(char *dst, size_t dlen, char *p)
38{
39 int i, o;
40
41 if (*p != '<')
42 return 0;
43
44 for (o = 0, i = 1; o < dlen; i++) {
45 if (p[i] == 0)
46 return 0;
47 if (p[i] == '>')
48 break;
49 dst[o++] = p[i];
50 }
51 dst[o] = 0;
52 return i + 1;
53}
54
232aca37 55// hmh..
57e4efe9 56static const char *known_types[] = {
3e52f54c 57 "const void *",
58 "void *",
59 "char *",
60 "FILE *",
232aca37 61 "unsigned __int8",
3e52f54c 62 "unsigned __int16",
232aca37 63 "unsigned int",
3e52f54c 64 "signed int",
65 "char",
66 "__int8",
67 "__int16",
68 "int",
69 "bool",
57e4efe9 70 "void",
232aca37 71 "BYTE",
72 "WORD",
57e4efe9 73 "DWORD",
74 "HMODULE",
75 "HANDLE",
76 "HWND",
232aca37 77 "LPCSTR",
78 "size_t",
57e4efe9 79};
80
81static int check_type(const char *name)
82{
83 int i, l;
84
232aca37 85 for (i = 0; i < ARRAY_SIZE(known_types); i++) {
57e4efe9 86 l = strlen(known_types[i]);
87 if (strncmp(known_types[i], name, l) == 0)
88 return l;
89 }
90
91 return 0;
92}
93
3e52f54c 94/* args are always expanded to 32bit */
95static const char *map_reg(const char *reg)
96{
97 const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
98 const char *regs_w[] = { "ax", "bx", "cx", "dx", "si", "di" };
99 const char *regs_b[] = { "al", "bl", "cl", "dl" };
100 int i;
101
102 for (i = 0; i < ARRAY_SIZE(regs_w); i++)
103 if (IS(reg, regs_w[i]))
104 return regs_f[i];
105
106 for (i = 0; i < ARRAY_SIZE(regs_b); i++)
107 if (IS(reg, regs_b[i]))
108 return regs_f[i];
109
110 return reg;
111}
112
232aca37 113static const char *hdrfn;
114static int pline = 0;
115
116static int parse_protostr(char *protostr, char **reglist, int *cnt_out,
117 int *is_stdcall)
118{
119 char regparm[16];
120 char buf[256];
3e52f54c 121 char cconv[32];
232aca37 122 int xarg = 0;
123 int ret;
124 char *p;
125
126 p = protostr;
127 if (p[0] == '/' && p[1] == '/') {
128 //printf("warning: decl for sym '%s' is commented out\n", sym);
129 p = sskip(p + 2);
130 }
131
132 ret = check_type(p);
133 if (ret <= 0) {
134 printf("%s:%d:%ld: unhandled return in '%s'\n",
135 hdrfn, pline, (p - protostr) + 1, protostr);
136 return 1;
137 }
138 p += ret;
139 p = sskip(p);
140
3e52f54c 141 p = next_word(cconv, sizeof(cconv), p);
232aca37 142 p = sskip(p);
3e52f54c 143 if (cconv[0] == 0) {
232aca37 144 printf("%s:%d:%ld: cconv missing\n",
145 hdrfn, pline, (p - protostr) + 1);
146 return 1;
147 }
3e52f54c 148 if (IS(cconv, "__cdecl"))
232aca37 149 *is_stdcall = 0;
3e52f54c 150 else if (IS(cconv, "__stdcall"))
151 *is_stdcall = 1;
152 else if (IS(cconv, "__fastcall"))
153 *is_stdcall = 1;
154 else if (IS(cconv, "__thiscall"))
232aca37 155 *is_stdcall = 1;
3e52f54c 156 else if (IS(cconv, "__userpurge"))
232aca37 157 *is_stdcall = 1; // in all cases seen..
3e52f54c 158 else if (IS(cconv, "__usercall"))
232aca37 159 *is_stdcall = 0; // ..or is it?
160 else {
232aca37 161 printf("%s:%d:%ld: unhandled cconv: '%s'\n",
3e52f54c 162 hdrfn, pline, (p - protostr) + 1, cconv);
232aca37 163 return 1;
164 }
165
166 p = next_idt(buf, sizeof(buf), p);
167 p = sskip(p);
168 if (buf[0] == 0) {
169 printf("%s:%d:%ld: func name missing\n",
170 hdrfn, pline, (p - protostr) + 1);
171 return 1;
172 }
173
174 ret = get_regparm(regparm, sizeof(regparm), p);
175 if (ret > 0) {
3e52f54c 176 if (!IS(regparm, "eax") && !IS(regparm, "ax")
177 && !IS(regparm, "al"))
232aca37 178 {
179 printf("%s:%d:%ld: bad regparm: %s\n",
180 hdrfn, pline, (p - protostr) + 1, regparm);
181 return 1;
182 }
183 p += ret;
184 p = sskip(p);
185 }
186
187 if (*p != '(') {
188 printf("%s:%d:%ld: '(' expected, got '%c'\n",
189 hdrfn, pline, (p - protostr) + 1, *p);
190 return 1;
191 }
192 p++;
193
194 while (1) {
195 p = sskip(p);
196 if (*p == ')')
197 break;
198 if (*p == ',')
199 p = sskip(p + 1);
200
201 xarg++;
202
203 ret = check_type(p);
204 if (ret <= 0) {
205 printf("%s:%d:%ld: unhandled type for arg%d\n",
206 hdrfn, pline, (p - protostr) + 1, xarg);
207 return 1;
208 }
209 p += ret;
210 p = sskip(p);
211
212 p = next_idt(buf, sizeof(buf), p);
213 p = sskip(p);
214#if 0
215 if (buf[0] == 0) {
216 printf("%s:%d:%ld: idt missing for arg%d\n",
217 hdrfn, pline, (p - protostr) + 1, xarg);
218 return 1;
219 }
220#endif
221 reglist[xarg - 1] = NULL;
222
223 ret = get_regparm(regparm, sizeof(regparm), p);
224 if (ret > 0) {
225 p += ret;
226 p = sskip(p);
227
3e52f54c 228 reglist[xarg - 1] = strdup(map_reg(regparm));
229 }
230 }
231
232 if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
233 if (reglist[0] != NULL) {
234 printf("%s:%d: %s with arg1 spec %s?\n",
235 hdrfn, pline, cconv, reglist[0]);
232aca37 236 }
3e52f54c 237 reglist[0] = strdup("ecx");
238 }
239
240 if (xarg > 1 && IS(cconv, "__fastcall")) {
241 if (reglist[1] != NULL) {
242 printf("%s:%d: %s with arg2 spec %s?\n",
243 hdrfn, pline, cconv, reglist[1]);
244 }
245 reglist[1] = strdup("edx");
232aca37 246 }
247
248 *cnt_out = xarg;
249
250 return 0;
251}
252
253static int is_x86_reg_saved(const char *reg)
254{
255 static const char *nosave_regs[] = { "eax", "edx", "ecx" };
256 int nosave = 0;
257 int r;
258
259 for (r = 0; r < ARRAY_SIZE(nosave_regs); r++)
260 if (strcmp(reg, nosave_regs[r]) == 0)
261 nosave = 1;
262
263 return !nosave;
264}
265
266static void out_toasm_x86(FILE *f, char *sym, char *reg_list[], int reg_cnt,
267 int is_stdcall)
268{
269 int have_normal = 0; // normal args
270 int have_regs = 0;
271 int must_save = 0;
272 int sarg_ofs = 1; // stack offset to args, in DWORDs
273 int args_repushed = 0;
274 int i;
275
276 for (i = 0; i < reg_cnt; i++) {
277 if (reg_list[i] == NULL) {
278 have_normal++;
279 continue;
280 }
281
282 have_regs++;
283 must_save |= is_x86_reg_saved(reg_list[i]);
284 }
285
a51421fa 286 fprintf(f, ".global _%s\n", sym);
287 fprintf(f, "_%s:\n", sym);
232aca37 288
289 if (!have_regs && !is_stdcall) {
290 fprintf(f, "\tjmp %s\n\n", sym);
291 return;
292 }
293
294 if (!have_normal && !must_save && !is_stdcall) {
295 // load arg regs
296 for (i = 0; i < reg_cnt; i++) {
297 fprintf(f, "\tmovl %d(%%esp), %%%s\n",
298 (i + sarg_ofs) * 4, reg_list[i]);
299 }
300 fprintf(f, "\tjmp %s\n\n", sym);
301 return;
302 }
303
304 // save the regs
305 for (i = 0; i < reg_cnt; i++) {
306 if (reg_list[i] != NULL && is_x86_reg_saved(reg_list[i])) {
307 fprintf(f, "\tpushl %%%s\n", reg_list[i]);
308 sarg_ofs++;
309 }
310 }
311
312 // reconstruct arg stack
313 for (i = reg_cnt - 1; i >= 0; i--) {
314 if (reg_list[i] == NULL) {
315 fprintf(f, "\tmovl %d(%%esp), %%eax\n",
316 (i + sarg_ofs) * 4);
317 fprintf(f, "\tpushl %%eax\n");
318 sarg_ofs++;
319 args_repushed++;
320 }
321 }
322 my_assert(args_repushed, have_normal);
323
324 // load arg regs
325 for (i = 0; i < reg_cnt; i++) {
326 if (reg_list[i] != NULL) {
327 fprintf(f, "\tmovl %d(%%esp), %%%s\n",
328 (i + sarg_ofs) * 4, reg_list[i]);
329 }
330 }
331
332 fprintf(f, "\n\t# %s\n", is_stdcall ? "__stdcall" : "__cdecl");
333 fprintf(f, "\tcall %s\n\n", sym);
334
335 if (args_repushed && !is_stdcall)
a51421fa 336 fprintf(f, "\tadd $%d,%%esp\n", args_repushed * 4);
232aca37 337
338 // restore regs
339 for (i = reg_cnt - 1; i >= 0; i--) {
340 if (reg_list[i] != NULL && is_x86_reg_saved(reg_list[i]))
341 fprintf(f, "\tpopl %%%s\n", reg_list[i]);
342 }
343
344 fprintf(f, "\tret\n\n");
345}
346
347static void out_fromasm_x86(FILE *f, char *sym, char *reg_list[], int reg_cnt,
348 int is_stdcall)
349{
350 int have_normal = 0; // normal args
351 int have_regs = 0;
352 int sarg_ofs = 1; // stack offset to args, in DWORDs
353 int stack_args;
354 int i;
355
356 for (i = 0; i < reg_cnt; i++) {
357 if (reg_list[i] == NULL) {
358 have_normal++;
359 continue;
360 }
361
362 have_regs++;
363 }
364
365 fprintf(f, "# %s\n", is_stdcall ? "__stdcall" : "__cdecl");
366 fprintf(f, ".global %s\n", sym);
367 fprintf(f, "%s:\n", sym);
368
3e52f54c 369 if (!have_regs && !is_stdcall) {
232aca37 370 fprintf(f, "\tjmp _%s\n\n", sym);
371 return;
372 }
373
374 fprintf(f, "\tpushl %%edx\n"); // just in case..
375 sarg_ofs++;
376
377 // construct arg stack
378 stack_args = have_normal;
379 for (i = reg_cnt - 1; i >= 0; i--) {
380 if (reg_list[i] == NULL) {
381 fprintf(f, "\tmovl %d(%%esp), %%edx\n",
382 (sarg_ofs + stack_args - 1) * 4);
383 fprintf(f, "\tpushl %%edx\n");
384 stack_args--;
385 }
386 else {
387 fprintf(f, "\tpushl %%%s\n", reg_list[i]);
388 }
389 sarg_ofs++;
390 }
391
392 // no worries about calling conventions - always __cdecl
393 fprintf(f, "\n\tcall _%s\n\n", sym);
394
395 if (sarg_ofs > 2)
a51421fa 396 fprintf(f, "\tadd $%d,%%esp\n", (sarg_ofs - 2) * 4);
232aca37 397
398 fprintf(f, "\tpopl %%edx\n");
399
400 if (is_stdcall && have_normal)
401 fprintf(f, "\tret $%d\n\n", have_normal * 4);
402 else
403 fprintf(f, "\tret\n\n");
404}
405
3e52f54c 406static void free_reglist(char *reg_list[], int reg_cnt)
407{
408 int i;
409
410 for (i = 0; i < reg_cnt; i++) {
411 if (reg_list[i] == NULL) {
412 free(reg_list[i]);
413 reg_list[i] = NULL;
414 }
415 }
416}
417
57e4efe9 418int main(int argc, char *argv[])
419{
232aca37 420 FILE *fout, *fsyms_to, *fsyms_from, *fhdr;
57e4efe9 421 char protostr[256];
422 char line[256];
423 char sym[256];
232aca37 424 char *reg_list[16];
425 int is_stdcall = 0;
426 int reg_cnt = 0;
57e4efe9 427 int ret;
428
232aca37 429 if (argc != 5) {
430 printf("usage:\n%s <bridge.s> <toasm_symf> <fromasm_symf> <hdrf>\n",
57e4efe9 431 argv[0]);
432 return 1;
433 }
434
232aca37 435 hdrfn = argv[4];
57e4efe9 436 fhdr = fopen(hdrfn, "r");
437 my_assert_not(fhdr, NULL);
438
232aca37 439 fsyms_from = fopen(argv[3], "r");
440 my_assert_not(fsyms_from, NULL);
441
442 fsyms_to = fopen(argv[2], "r");
443 my_assert_not(fsyms_to, NULL);
57e4efe9 444
445 fout = fopen(argv[1], "w");
446 my_assert_not(fout, NULL);
447
448 fprintf(fout, ".text\n\n");
232aca37 449 fprintf(fout, "# to asm\n\n");
57e4efe9 450
232aca37 451 while (fgets(line, sizeof(line), fsyms_to))
57e4efe9 452 {
453 next_word(sym, sizeof(sym), line);
454 if (sym[0] == 0 || sym[0] == ';' || sym[0] == '#')
455 continue;
456
457 ret = find_protostr(protostr, sizeof(protostr), fhdr,
458 sym, &pline);
459 if (ret != 0) {
460 printf("%s: sym '%s' is missing\n",
461 hdrfn, sym);
232aca37 462 goto out;
57e4efe9 463 }
464
232aca37 465 ret = parse_protostr(protostr, reg_list, &reg_cnt, &is_stdcall);
466 if (ret)
467 goto out;
57e4efe9 468
232aca37 469 out_toasm_x86(fout, sym, reg_list, reg_cnt, is_stdcall);
3e52f54c 470 free_reglist(reg_list, reg_cnt);
232aca37 471 }
57e4efe9 472
232aca37 473 fprintf(fout, "# from asm\n\n");
57e4efe9 474
232aca37 475 while (fgets(line, sizeof(line), fsyms_from))
476 {
477 next_word(sym, sizeof(sym), line);
478 if (sym[0] == 0 || sym[0] == ';' || sym[0] == '#')
479 continue;
57e4efe9 480
232aca37 481 ret = find_protostr(protostr, sizeof(protostr), fhdr,
482 sym, &pline);
483 if (ret != 0) {
484 printf("%s: sym '%s' is missing\n",
485 hdrfn, sym);
486 goto out;
57e4efe9 487 }
57e4efe9 488
232aca37 489 ret = parse_protostr(protostr, reg_list, &reg_cnt, &is_stdcall);
490 if (ret)
491 goto out;
57e4efe9 492
232aca37 493 out_fromasm_x86(fout, sym, reg_list, reg_cnt, is_stdcall);
3e52f54c 494 free_reglist(reg_list, reg_cnt);
57e4efe9 495 }
496
232aca37 497 ret = 0;
498out:
57e4efe9 499 fclose(fout);
232aca37 500 fclose(fsyms_to);
501 fclose(fsyms_from);
502 fclose(fhdr);
503 if (ret)
504 remove(argv[1]);
505
506 return ret;
57e4efe9 507}