From 39b168b8949dfc08ec521a70cdc4440dce1ef207 Mon Sep 17 00:00:00 2001 From: notaz Date: Mon, 16 Dec 2013 02:39:05 +0200 Subject: [PATCH] more wip, handle C calls --- stdc.hlst | 88 +++++++++++++++++++++++++ tools/protoparse.h | 159 ++++++++++++++++++++++++++++++++++++--------- tools/translate.c | 103 ++++++++++++++++++++++------- 3 files changed, 298 insertions(+), 52 deletions(-) create mode 100644 stdc.hlst diff --git a/stdc.hlst b/stdc.hlst new file mode 100644 index 0000000..260c869 --- /dev/null +++ b/stdc.hlst @@ -0,0 +1,88 @@ +int __cdecl rand (void); +void __cdecl srand (unsigned int); +void* __cdecl calloc (size_t, size_t); +void* __cdecl malloc (size_t); +void* __cdecl realloc (void*, size_t); +void __cdecl free (void*); +void __cdecl abort (void); +void __cdecl exit (int); +int __cdecl atexit (void (__cdecl *)(void)); +int __cdecl system (const char*); +char* __cdecl getenv (const char*); +void* __cdecl bsearch (const void*, const void*, size_t, size_t, int (__cdecl *)(const void*, const void*)); +void __cdecl qsort(void*, size_t, size_t, int (__cdecl *)(const void*, const void*)); +int __cdecl abs (int); +long __cdecl labs (long); +long __cdecl strtol (const char*, char**, int); +unsigned long __cdecl strtoul (const char*, char**, int); + +void* __cdecl memchr (const void*, int, size_t); +int __cdecl memcmp (const void*, const void*, size_t); +void* __cdecl memcpy (void*, const void*, size_t); +void* __cdecl memmove (void*, const void*, size_t); +void* __cdecl memset (void*, int, size_t); +char* __cdecl strcat (char*, const char*); +char* __cdecl strchr (const char*, int) ; +int __cdecl strcmp (const char*, const char*); +int __cdecl strcoll (const char*, const char*); +char* __cdecl strcpy (char*, const char*); +size_t __cdecl strcspn (const char*, const char*); +char* __cdecl strerror (int); +double __cdecl atof (const char*); +int __cdecl atoi (const char*); +long __cdecl atol (const char*); + +size_t __cdecl strlen (const char*) ; +char* __cdecl strncat (char*, const char*, size_t); +int __cdecl strncmp (const char*, const char*, size_t) ; +char* __cdecl strncpy (char*, const char*, size_t); +char* __cdecl strpbrk (const char*, const char*) ; +char* __cdecl strrchr (const char*, int) ; +size_t __cdecl strspn (const char*, const char*) ; +char* __cdecl strstr (const char*, const char*) ; +char* __cdecl strtok (char*, const char*); +size_t __cdecl strxfrm (char*, const char*, size_t); + +int __cdecl isalnum(int); +int __cdecl isalpha(int); +int __cdecl iscntrl(int); +int __cdecl isdigit(int); +int __cdecl isgraph(int); +int __cdecl islower(int); +int __cdecl isprint(int); +int __cdecl ispunct(int); +int __cdecl isspace(int); +int __cdecl isupper(int); +int __cdecl isxdigit(int); +int __cdecl tolower(int); +int __cdecl toupper(int); + +char* __cdecl asctime (const struct tm*); +char* __cdecl ctime (const time_t*); +struct tm* __cdecl gmtime (const time_t*); +struct tm* __cdecl localtime (const time_t*); +size_t __cdecl strftime (char*, size_t, const char*, const struct tm*); +extern void __cdecl _tzset (void); +extern void __cdecl tzset (void); + +FILE* __cdecl fopen (const char*, const char*); +FILE* __cdecl freopen (const char*, const char*, FILE*); +int __cdecl fflush (FILE*); +int __cdecl fclose (FILE*); +int __cdecl remove (const char*); +int __cdecl rename (const char*, const char*); +FILE* __cdecl tmpfile (void); +char* __cdecl tmpnam (char*); +char* __cdecl _tempnam (const char*, const char*); +int __cdecl _rmtmp(void); +int __cdecl _unlink (const char*); +char* __cdecl tempnam (const char*, const char*); +int __cdecl rmtmp(void); +int __cdecl unlink (const char*); +int __cdecl setvbuf (FILE*, char*, int, size_t); +void __cdecl setbuf (FILE*, char*); +size_t __cdecl fread (void*, size_t, size_t, FILE*); +size_t __cdecl fwrite (const void*, size_t, size_t, FILE*); +int __cdecl fseek (FILE*, long, int); +long __cdecl ftell (FILE*); +void __cdecl rewind (FILE*); diff --git a/tools/protoparse.h b/tools/protoparse.h index c102c49..533dd46 100644 --- a/tools/protoparse.h +++ b/tools/protoparse.h @@ -1,31 +1,68 @@ +struct parsed_proto; + +struct parsed_proto_arg { + char *reg; + const char *type; + struct parsed_proto *fptr; + void *datap; +}; + struct parsed_proto { - struct { - char *reg; - const char *type; - void *datap; - } arg[16]; + char name[256]; const char *ret_type; - int is_stdcall; + struct parsed_proto_arg arg[16]; int argc; int argc_stack; int argc_reg; + unsigned int is_stdcall:1; + unsigned int is_fptr:1; }; static const char *hdrfn; static int hdrfline = 0; -static int find_protostr(char *dst, size_t dlen, FILE *fhdr, const char *sym) +static int find_protostr(char *dst, size_t dlen, FILE *fhdr, + const char *fname, const char *sym_) { + const char *sym = sym_; + FILE *finc; + int symlen; int line = 0; + int ret; char *p; + if (sym[0] == '_' && strncmp(fname, "stdc", 4) == 0) + sym++; + symlen = strlen(sym); + rewind(fhdr); while (fgets(dst, dlen, fhdr)) { line++; - if (strstr(dst, sym) != NULL) + if (strncmp(dst, "#include ", 9) == 0) { + p = strpbrk(dst + 9, "\r\n "); + if (p != NULL) + *p = 0; + + finc = fopen(dst + 9, "r"); + if (finc == NULL) { + printf("%s:%d: can't open '%s'\n", + fname, line, dst + 9); + continue; + } + ret = find_protostr(dst, dlen, finc, + dst + 9, sym_); + fclose(finc); + if (ret == 0) + break; + continue; + } + + p = strstr(dst, sym); + if (p != NULL + && (my_isblank(p[symlen]) || my_issep(p[symlen]))) break; } hdrfline = line; @@ -86,13 +123,26 @@ static const char *known_types[] = { "size_t", }; +static int typecmp(const char *n, const char *t) +{ + for (; *t != 0; n++, t++) { + while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*')) + n++; + while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*')) + t++; + if (*n != *t) + return *n - *t; + } + + return 0; +} + static const char *check_type(const char *name) { - int i, l; + int i; for (i = 0; i < ARRAY_SIZE(known_types); i++) { - l = strlen(known_types[i]); - if (strncmp(known_types[i], name, l) == 0) + if (typecmp(name, known_types[i]) == 0) return known_types[i]; } @@ -120,13 +170,14 @@ static const char *map_reg(const char *reg) static int parse_protostr(char *protostr, struct parsed_proto *pp) { + struct parsed_proto_arg *arg; char regparm[16]; char buf[256]; char cconv[32]; const char *kt; int xarg = 0; + char *p, *p1; int ret; - char *p; int i; p = protostr; @@ -139,18 +190,23 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp) if (kt == NULL) { printf("%s:%d:%ld: unhandled return in '%s'\n", hdrfn, hdrfline, (p - protostr) + 1, protostr); - return 1; + return -1; } pp->ret_type = kt; p += strlen(kt); p = sskip(p); + if (*p == '(') { + pp->is_fptr = 1; + p = sskip(p + 1); + } + p = next_word(cconv, sizeof(cconv), p); p = sskip(p); if (cconv[0] == 0) { printf("%s:%d:%ld: cconv missing\n", hdrfn, hdrfline, (p - protostr) + 1); - return 1; + return -1; } if (IS(cconv, "__cdecl")) pp->is_stdcall = 0; @@ -167,16 +223,26 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp) else { printf("%s:%d:%ld: unhandled cconv: '%s'\n", hdrfn, hdrfline, (p - protostr) + 1, cconv); - return 1; + return -1; + } + + if (pp->is_fptr) { + if (*p != '*') { + printf("%s:%d:%ld: '*' expected\n", + hdrfn, hdrfline, (p - protostr) + 1); + return -1; + } + p = sskip(p + 1); } p = next_idt(buf, sizeof(buf), p); p = sskip(p); if (buf[0] == 0) { printf("%s:%d:%ld: func name missing\n", - hdrfn, hdrfline, (p - protostr) + 1); - return 1; + hdrfn, hdrfline, (p - protostr) + 1); + return -1; } + strcpy(pp->name, buf); ret = get_regparm(regparm, sizeof(regparm), p); if (ret > 0) { @@ -185,55 +251,88 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp) { printf("%s:%d:%ld: bad regparm: %s\n", hdrfn, hdrfline, (p - protostr) + 1, regparm); - return 1; + return -1; } p += ret; p = sskip(p); } + if (pp->is_fptr) { + if (*p != ')') { + printf("%s:%d:%ld: ')' expected\n", + hdrfn, hdrfline, (p - protostr) + 1); + return -1; + } + p = sskip(p + 1); + } + if (*p != '(') { printf("%s:%d:%ld: '(' expected, got '%c'\n", hdrfn, hdrfline, (p - protostr) + 1, *p); - return 1; + return -1; } p++; + // check for x(void) + p = sskip(p); + if (strncmp(p, "void", 4) == 0 && *sskip(p + 4) == ')') + p += 4; + while (1) { p = sskip(p); - if (*p == ')') + if (*p == ')') { + p++; break; + } if (*p == ',') p = sskip(p + 1); + arg = &pp->arg[xarg]; xarg++; + p1 = p; kt = check_type(p); if (kt == NULL) { printf("%s:%d:%ld: unhandled type for arg%d\n", hdrfn, hdrfline, (p - protostr) + 1, xarg); - return 1; + return -1; } - pp->arg[xarg - 1].type = kt; + arg->type = kt; p += strlen(kt); p = sskip(p); + if (*p == '(') { + // func ptr + arg->fptr = calloc(1, sizeof(*arg->fptr)); + ret = parse_protostr(p1, arg->fptr); + if (ret < 0) { + printf("%s:%d:%ld: funcarg parse failed\n", + hdrfn, hdrfline, p1 - protostr); + return -1; + } + // we'll treat it as void * for non-calls + arg->type = "void *"; + + p = p1 + ret; + } + p = next_idt(buf, sizeof(buf), p); p = sskip(p); #if 0 if (buf[0] == 0) { printf("%s:%d:%ld: idt missing for arg%d\n", hdrfn, hdrfline, (p - protostr) + 1, xarg); - return 1; + return -1; } #endif - pp->arg[xarg - 1].reg = NULL; + arg->reg = NULL; ret = get_regparm(regparm, sizeof(regparm), p); if (ret > 0) { p += ret; p = sskip(p); - pp->arg[xarg - 1].reg = strdup(map_reg(regparm)); + arg->reg = strdup(map_reg(regparm)); } } @@ -262,7 +361,7 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp) pp->argc_reg++; } - return 0; + return p - protostr; } static int proto_parse(FILE *fhdr, const char *sym, struct parsed_proto *pp) @@ -272,13 +371,13 @@ static int proto_parse(FILE *fhdr, const char *sym, struct parsed_proto *pp) memset(pp, 0, sizeof(*pp)); - ret = find_protostr(protostr, sizeof(protostr), fhdr, sym); + ret = find_protostr(protostr, sizeof(protostr), fhdr, hdrfn, sym); if (ret != 0) { printf("%s: sym '%s' is missing\n", hdrfn, sym); return ret; } - return parse_protostr(protostr, pp); + return parse_protostr(protostr, pp) < 0 ? -1 : 0; } static void proto_release(struct parsed_proto *pp) @@ -286,7 +385,9 @@ static void proto_release(struct parsed_proto *pp) int i; for (i = 0; i < pp->argc; i++) { - if (pp->arg[i].reg == NULL) + if (pp->arg[i].reg != NULL) free(pp->arg[i].reg); + if (pp->arg[i].fptr != NULL) + free(pp->arg[i].fptr); } } diff --git a/tools/translate.c b/tools/translate.c index fbc8ff6..532c160 100644 --- a/tools/translate.c +++ b/tools/translate.c @@ -8,6 +8,7 @@ #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) #define IS(w, y) !strcmp(w, y) +#define IS_START(w, y) !strncmp(w, y, strlen(y)) #include "protoparse.h" @@ -294,7 +295,7 @@ static int parse_indmode(char *name, int *regmask, int need_c_cvt) *d = 0; // skip 'ds:' prefix - if (!strncmp(s, "ds:", 3)) + if (IS_START(s, "ds:")) s += 3; s = next_idt(w, sizeof(w), s); @@ -334,12 +335,12 @@ static const char *parse_stack_el(const char *name) long val; int len; - if (!strncmp(name, "ebp+", 4) + if (IS_START(name, "ebp+") && !('0' <= name[4] && name[4] <= '9')) { return name + 4; } - if (strncmp(name, "esp+", 4) != 0) + if (!IS_START(name, "esp+")) return NULL; p = strchr(name + 4, '+'); @@ -399,7 +400,8 @@ static void setup_reg_opr(struct parsed_opr *opr, int reg, enum opr_lenmod lmod, *regmask |= 1 << reg; } -static struct parsed_equ *equ_find(struct parsed_op *po, const char *name); +static struct parsed_equ *equ_find(struct parsed_op *po, const char *name, + int *extra_offs); static int parse_operand(struct parsed_opr *opr, int *regmask, int *regmask_indirect, @@ -409,6 +411,7 @@ static int parse_operand(struct parsed_opr *opr, int ret, len; long number; int wordc_in; + char *tmp; int i; if (w >= wordc) @@ -444,6 +447,8 @@ static int parse_operand(struct parsed_opr *opr, if (label != NULL) { opr->type = OPT_LABEL; + if (IS_START(label, "ds:")) + label += 3; strcpy(opr->name, label); return wordc; } @@ -464,16 +469,30 @@ static int parse_operand(struct parsed_opr *opr, } } - if (wordc_in == 2 && IS(words[w], "offset")) { - opr->type = OPT_OFFSET; - strcpy(opr->name, words[w + 1]); - return wordc; + if (wordc_in == 2) { + if (IS(words[w], "offset")) { + opr->type = OPT_OFFSET; + strcpy(opr->name, words[w + 1]); + return wordc; + } + if (IS(words[w], "(offset")) { + char *p = strchr(words[w + 1], ')'); + if (p == NULL) + aerr("parse of bracketed offset failed\n"); + *p = 0; + opr->type = OPT_OFFSET; + strcpy(opr->name, words[w + 1]); + return wordc; + } } if (wordc_in != 1) aerr("parse_operand 1 word expected\n"); - strcpy(opr->name, words[w]); + tmp = words[w]; + if (IS_START(tmp, "ds:")) + tmp += 3; + strcpy(opr->name, tmp); if (words[w][0] == '[') { opr->type = OPT_REGMEM; @@ -485,7 +504,7 @@ static int parse_operand(struct parsed_opr *opr, if (opr->lmod == OPLM_UNSPEC && parse_stack_el(opr->name)) { // might be an equ struct parsed_equ *eq = - equ_find(NULL, parse_stack_el(opr->name)); + equ_find(NULL, parse_stack_el(opr->name), &i); if (eq) opr->lmod = eq->lmod; } @@ -905,12 +924,33 @@ static const char *opr_reg_p(struct parsed_op *po, struct parsed_opr *popr) return regs_r32[popr->reg]; } -static struct parsed_equ *equ_find(struct parsed_op *po, const char *name) +static struct parsed_equ *equ_find(struct parsed_op *po, const char *name, + int *extra_offs) { + const char *p; + char *endp; + int namelen; int i; + *extra_offs = 0; + namelen = strlen(name); + + p = strchr(name, '+'); + if (p != NULL) { + namelen = p - name; + if (namelen <= 0) + ferr(po, "equ parse failed for '%s'\n", name); + + if (IS_START(p, "0x")) + p += 2; + *extra_offs = strtol(p, &endp, 16); + if (*endp != 0) + ferr(po, "equ parse failed for '%s'\n", name); + } + for (i = 0; i < g_eqcnt; i++) - if (IS(g_eqs[i].name, name)) + if (strncmp(g_eqs[i].name, name, namelen) == 0 + && g_eqs[i].name[namelen] == 0) break; if (i >= g_eqcnt) { if (po != NULL) @@ -930,25 +970,28 @@ static void stack_frame_access(struct parsed_op *po, int i, arg_i, arg_s; const char *bp_arg; int stack_ra = 0; + int offset = 0; int sf_ofs; bp_arg = parse_stack_el(name); snprintf(g_comment, sizeof(g_comment), "%s", bp_arg); - eq = equ_find(po, bp_arg); + eq = equ_find(po, bp_arg, &offset); if (eq == NULL) ferr(po, "detected but missing eq\n"); + offset += eq->offset; + if (!strncmp(name, "ebp", 3)) stack_ra = 4; - if (stack_ra <= eq->offset && eq->offset < stack_ra + 4) - ferr(po, "reference to ra? %d %d\n", eq->offset, stack_ra); + if (stack_ra <= offset && offset < stack_ra + 4) + ferr(po, "reference to ra? %d %d\n", offset, stack_ra); - if (eq->offset > stack_ra) { - arg_i = (eq->offset - stack_ra - 4) / 4; + if (offset > stack_ra) { + arg_i = (offset - stack_ra - 4) / 4; if (arg_i < 0 || arg_i >= g_func_pp.argc_stack) ferr(po, "offset %d (%s) doesn't map to any arg\n", - eq->offset, bp_arg); + offset, bp_arg); for (i = arg_s = 0; i < g_func_pp.argc; i++) { if (g_func_pp.arg[i].reg != NULL) @@ -968,9 +1011,9 @@ static void stack_frame_access(struct parsed_op *po, if (g_stack_fsz == 0) ferr(po, "stack var access without stackframe\n"); - sf_ofs = g_stack_fsz + eq->offset; + sf_ofs = g_stack_fsz + offset; if (sf_ofs < 0) - ferr(po, "bp_stack offset %d/%d\n", eq->offset, g_stack_fsz); + ferr(po, "bp_stack offset %d/%d\n", offset, g_stack_fsz); if (is_lea) prefix = "(u32)&"; @@ -1744,6 +1787,15 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) ops[ret].flags |= OPF_RMD; } + // can't call functions with non-__cdecl callbacks yet + for (arg = 0; arg < pp->argc; arg++) { + if (pp->arg[arg].fptr != NULL) { + pp_tmp = pp->arg[arg].fptr; + if (pp_tmp->is_stdcall || pp_tmp->argc != pp_tmp->argc_stack) + ferr(po, "'%s' has a non-__cdecl callback\n", tmpname); + } + } + for (arg = 0; arg < pp->argc; arg++) if (pp->arg[arg].reg == NULL) break; @@ -2334,10 +2386,15 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) fprintf(fout, "(u32)"); } - if (po->operand[0].type != OPT_LABEL) + if (po->operand[0].type != OPT_LABEL) { fprintf(fout, "icall%d(", i); - else - fprintf(fout, "%s(", opr_name(po, 0)); + } + else { + if (pp->name[0] == 0) + ferr(po, "missing pp->name\n"); + fprintf(fout, "%s(", pp->name); + } + for (arg = 0; arg < pp->argc; arg++) { if (arg > 0) fprintf(fout, ", "); -- 2.39.2