diff --git a/tcc.c b/tcc.c index 4fe66952..5f3f06a8 100644 --- a/tcc.c +++ b/tcc.c @@ -71,6 +71,17 @@ typedef struct SymStack { struct Sym *hash[SYM_HASH_SIZE]; } SymStack; +/* relocation entry (currently only used for functions or variables */ +typedef struct Reloc { + int type; /* type of relocation */ + int addr; /* address of relocation */ + struct Reloc *next; /* next relocation */ +} Reloc; + +#define RELOC_ADDR32 1 /* 32 bits relocation */ +#define RELOC_REL32 2 /* 32 bits relative relocation */ + + #define SYM_STRUCT 0x40000000 /* struct/union/enum symbol space */ #define SYM_FIELD 0x20000000 /* struct/union field symbol space */ @@ -144,13 +155,15 @@ int nb_include_paths; #define VT_ENUM 0x00800 /* enum definition */ #define VT_FUNC 0x01000 /* function type */ #define VT_STRUCT 0x002000 /* struct/union definition */ -#define VT_TYPEDEF 0x004000 /* typedef definition */ -#define VT_EXTERN 0x008000 /* extern definition */ -#define VT_STATIC 0x010000 /* static variable */ -#define VT_SHORT 0x020000 /* short type */ +#define VT_SHORT 0x004000 /* short type */ #define VT_STRUCT_SHIFT 18 /* structure/enum name shift (14 bits left) */ -#define VT_TYPE 0xffffffc0 /* type mask */ +/* storage */ +#define VT_EXTERN 0x00008000 /* extern definition */ +#define VT_STATIC 0x00010000 /* static variable */ +#define VT_TYPEDEF 0x00020000 /* typedef definition */ + +#define VT_TYPE 0xfffc7fc0 /* type mask */ #define VT_TYPEN 0x0000003f /* ~VT_TYPE */ #define VT_FUNCN -4097 /* ~VT_FUNC */ @@ -269,12 +282,12 @@ enum { }; void sum(); -void next(); +void next(void); void next_nomacro(); int expr_const(); void expr_eq(); -void expr(); -void decl(); +void expr(void); +void decl(int l); void decl_initializer(int t, int c, int first, int size_only); int decl_initializer_alloc(int t, int has_init); int gv(void); @@ -1450,12 +1463,12 @@ typedef struct GFuncContext { int args_size; } GFuncContext; -void g(c) +void g(int c) { *(char *)ind++ = c; } -void o(c) +void o(int c) { while (c) { g(c); @@ -1463,6 +1476,49 @@ void o(c) } } +void gen_le32(int c) +{ + g(c); + g(c >> 8); + g(c >> 16); + g(c >> 24); +} + +/* add a new relocation entry to symbol 's' */ +void greloc(Sym *s, int addr, int type) +{ + Reloc *p; + p = malloc(sizeof(Reloc)); + if (!p) + error("memory full"); + p->type = type; + p->addr = addr; + p->next = (Reloc *)s->c; + s->c = (int)p; +} + +/* patch each relocation entry with value 'val' */ +void greloc_patch(Sym *s, int val) +{ + Reloc *p, *p1; + + p = (Reloc *)s->c; + while (p != NULL) { + p1 = p->next; + switch(p->type) { + case RELOC_ADDR32: + *(int *)p->addr = val; + break; + case RELOC_REL32: + *(int *)p->addr = val - p->addr - 4; + break; + } + free(p); + p = p1; + } + s->c = val; +} + /* output a symbol and patch all calls to it */ void gsym_addr(t, a) { @@ -1484,7 +1540,7 @@ void gsym(t) #define psym oad /* instruction + 4 bytes data. Return the address of the data */ -int oad(c, s) +int oad(int c, int s) { o(c); *(int *)ind = s; @@ -1493,6 +1549,17 @@ int oad(c, s) return s; } +/* output constant with relocation if 't & VT_FORWARD' is true */ +void gen_addr32(int c, int t) +{ + if (!(t & VT_FORWARD)) { + gen_le32(c); + } else { + greloc((Sym *)c, ind, RELOC_ADDR32); + gen_le32(0); + } +} + /* XXX: generate correct pointer for forward references to functions */ /* r = (ft, fc) */ void load(r, ft, fc) @@ -1516,7 +1583,8 @@ void load(r, ft, fc) else o(0x8b); /* movl */ if (v == VT_CONST) { - oad(0x05 + r * 8, fc); /* 0xXX, r */ + o(0x05 + r * 8); /* 0xXX, r */ + gen_addr32(fc, ft); } else if (v == VT_LOCAL) { oad(0x85 + r * 8, fc); /* xx(%ebp), r */ } else { @@ -1524,7 +1592,8 @@ void load(r, ft, fc) } } else { if (v == VT_CONST) { - oad(0xb8 + r, fc); /* mov $xx, r */ + o(0xb8 + r); /* mov $xx, r */ + gen_addr32(fc, ft); } else if (v == VT_LOCAL) { o(0x8d); oad(0x85 + r * 8, fc); /* lea xxx(%ebp), r */ @@ -1559,7 +1628,8 @@ void store(r, ft, fc) o(0x66); o(0x89 - b); if (fr == VT_CONST) { - oad(0x05 + r * 8, fc); /* mov r,xxx */ + o(0x05 + r * 8); /* mov r,xxx */ + gen_addr32(fc, ft); } else if (fr == VT_LOCAL) { oad(0x85 + r * 8, fc); /* mov r,xxx(%ebp) */ } else if (ft & VT_LVAL) { @@ -1615,9 +1685,11 @@ void gfunc_call(GFuncContext *c, int ft, int fc) if (r == VT_CONST) { /* forward reference */ if (ft & VT_FORWARD) { - *(int *)fc = psym(0xe8, *(int *)fc); - } else + greloc((Sym *)fc, ind + 1, RELOC_REL32); + oad(0xe8, 0); + } else { oad(0xe8, fc - ind - 5); + } } else if (r == VT_LOCAL) { oad(0x95ff, fc); /* call *xxx(%ebp) */ } else { @@ -2171,7 +2243,7 @@ void inc(post, c) } /* enum/struct/union declaration */ -int struct_decl(u) +int struct_decl(int u) { int a, t, b, v, size, align, maxalign, c; Sym *s, *ss, **ps; @@ -2428,27 +2500,19 @@ int type_decl(int *v, int t, int td) } /* define a new external reference to a function 'v' of type 'u' */ -Sym *external_func(v, u) +Sym *external_sym(int v, int u) { - int n; Sym *s; s = sym_find(v); if (!s) { - n = (int)dlsym(0, get_tok_str(v, 0)); - if (n == 0) { - /* used to generate symbol list */ - s = sym_push1(&global_stack, - v, u | VT_CONST | VT_LVAL | VT_FORWARD, 0); - } else { - /* int f() */ - s = sym_push1(&global_stack, - v, u | VT_CONST | VT_LVAL, n); - } + /* push forward reference */ + s = sym_push1(&global_stack, + v, u | VT_CONST | VT_FORWARD, 0); } return s; } -void indir() +void indir(void) { if (vt & VT_LVAL) gv(); @@ -2459,7 +2523,7 @@ void indir() vt |= VT_LVAL; } -void unary() +void unary(void) { int n, t, ft, fc, p, align, size; Sym *s; @@ -2523,7 +2587,12 @@ void unary() indir(); } else if (t == '&') { unary(); - test_lvalue(); + /* functions names must be treated as function pointers, + except for unary '&' and sizeof. Since we consider that + functions are not lvalues, we only have to handle it + there and in function calls. */ + if (!(vt & VT_FUNC)) + test_lvalue(); vt = mk_pointer(vt & VT_LVALN); } else if (t == '!') { @@ -2577,12 +2646,12 @@ void unary() p = anon_sym++; sym_push1(&global_stack, p, 0, FUNC_OLD); /* int() function */ - s = external_func(t, VT_FUNC | (p << VT_STRUCT_SHIFT)); + s = external_sym(t, VT_FUNC | (p << VT_STRUCT_SHIFT)); } vset(s->t, s->c); - /* if forward reference, we must point to s->c */ + /* if forward reference, we must point to s */ if (vt & VT_FORWARD) - vc = (int)&s->c; + vc = (int)s; } } @@ -2632,8 +2701,16 @@ void unary() int rett, retc; /* function call */ - if (!(vt & VT_FUNC)) - expect("function type"); + if (!(vt & VT_FUNC)) { + if ((vt & (VT_PTR | VT_ARRAY)) == VT_PTR) { + vt = pointed_type(vt); + if (!(vt & VT_FUNC)) + goto error_func; + } else { + error_func: + expect("function type"); + } + } /* get return type */ s = sym_find((unsigned)vt >> VT_STRUCT_SHIFT); @@ -2725,6 +2802,30 @@ void unary() } } +/* check if types are compatible for assignation */ +int same_types(int t1, int t2) +{ + t1 &= VT_TYPE; + t2 &= VT_TYPE; + if (t1 & VT_PTR) { + /* XXX: zero test ? */ + if (!(t2 & VT_PTR)) + return 0; + t1 = pointed_type(t1); + t2 = pointed_type(t2); + /* void matches everything */ + if (t1 == VT_VOID || t2 == VT_VOID) + return 1; + return same_types(t1, t2); + } else if (t1 & VT_STRUCT) { + return (t2 == t1); + } else { + /* XXX: not complete */ + return 1; + } +} + + void uneq() { int t; @@ -2740,9 +2841,8 @@ void uneq() next(); if (t == '=') { expr_eq(); - /* XXX: be more precise */ - if ((vt & VT_PTR) != (vstack_ptr[-2] & VT_PTR)) - warning("incompatible type"); + if (!same_types(vt, vstack_ptr[-2])) + error("incompatible types"); } else { vpush(); expr_eq(); @@ -2919,7 +3019,8 @@ void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_reg) s = local_stack.top; while (tok != '}') { decl(VT_LOCAL); - block(bsym, csym, case_sym, def_sym, case_reg); + if (tok != '}') + block(bsym, csym, case_sym, def_sym, case_reg); } /* pop locally defined symbols */ sym_pop(&local_stack, s); @@ -3002,6 +3103,7 @@ void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_reg) gsym_addr(c, d); skip(')'); gsym(a); + skip(';'); } else if (tok == TOK_SWITCH) { next(); @@ -3297,9 +3399,9 @@ void decl_initializer(int t, int c, int first, int size_only) /* patch type size if needed */ if (n < 0) s->c = array_length; - } else if (t & VT_STRUCT) { + } else if ((t & VT_STRUCT) && tok == '{') { /* XXX: union needs only one init */ - skip('{'); + next(); s = sym_find(((unsigned)t >> VT_STRUCT_SHIFT) | SYM_STRUCT); f = s->next; array_length = 0; @@ -3428,14 +3530,20 @@ int decl_initializer_alloc(int t, int has_init) /* 'l' is VT_LOCAL or VT_CONST to define default storage type */ -void decl(l) +void decl(int l) { - int *a, t, b, v, u, n, addr, has_init, size, align; + int *a, t, b, v, u, addr, has_init, size, align; Sym *sym; while (1) { b = ist(); if (!b) { + /* skip redundant ';' */ + /* XXX: find more elegant solution */ + if (tok == ';') { + next(); + continue; + } /* special test for old K&R protos without explicit int type. Only accepted when defining global data */ if (l == VT_LOCAL || tok < TOK_DEFINE) @@ -3450,16 +3558,17 @@ void decl(l) while (1) { /* iterate thru each declaration */ t = type_decl(&v, b, TYPE_DIRECT); if (tok == '{') { + if (l == VT_LOCAL) + error("cannot use local functions"); if (!(t & VT_FUNC)) expect("function definition"); /* patch forward references */ if ((sym = sym_find(v)) && (sym->t & VT_FORWARD)) { - gsym(sym->c); - sym->c = ind; - sym->t = VT_CONST | VT_LVAL | t; + greloc_patch(sym, ind); + sym->t = VT_CONST | t; } else { /* put function address */ - sym_push1(&global_stack, v, VT_CONST | VT_LVAL | t, ind); + sym_push1(&global_stack, v, VT_CONST | t, ind); } funcname = get_tok_str(v, 0); /* push a dummy symbol to enable local sym storage */ @@ -3516,21 +3625,15 @@ void decl(l) /* save typedefed type */ sym_push(v, t | VT_TYPEDEF, 0); } else if (t & VT_FUNC) { - /* XXX: incorrect to flush, but needed while - waiting for function prototypes */ /* external function definition */ - external_func(v, t); + external_sym(v, t); } else { /* not lvalue if array */ if (!(t & VT_ARRAY)) t |= VT_LVAL; if (b & VT_EXTERN) { /* external variable */ - /* XXX: factorize with external function def */ - n = (int)dlsym(NULL, get_tok_str(v, 0)); - if (!n) - error("unknown external variable"); - sym_push(v, VT_CONST | t, n); + external_sym(v, t); } else { u = l; if (t & VT_STATIC) @@ -3566,6 +3669,30 @@ void open_dll(char *libname) error((char *)dlerror()); } +void reloc_external_syms(void) +{ + Sym *s, *s1; + char *str; + int addr; + + s = global_stack.top; + while (s != NULL) { + s1 = s->prev; + if (s->t & VT_FORWARD) { + /* if there is at least one relocation to do, then find it + and patch it */ + if (s->c) { + str = get_tok_str(s->v, 0); + addr = (int)dlsym(NULL, str); + if (!addr) + error("unresolved external reference '%s'", str); + greloc_patch(s, addr); + } + } + s = s1; + } +} + /* output a binary file (for testing) */ void build_exe(char *filename) { @@ -3658,6 +3785,7 @@ int main(int argc, char **argv) decl(VT_CONST); if (tok != -1) expect("declaration"); + reloc_external_syms(); if (outfile) { build_exe(outfile); return 0;