mirror of
https://github.com/mirror/tinycc.git
synced 2025-03-08 08:40:08 +08:00
better external allocs handling
This commit is contained in:
parent
18a8013fe7
commit
8523baf61d
242
tcc.c
242
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;
|
||||
|
Loading…
Reference in New Issue
Block a user