added computed gotos - better runtime error handling

This commit is contained in:
bellard 2002-11-03 00:43:55 +00:00
parent fde824b7cd
commit 804d0c8d41

248
tcc.c
View file

@ -190,7 +190,7 @@ typedef struct AttributeDef {
#define MACRO_OBJ 0 /* object like macro */
#define MACRO_FUNC 1 /* function like macro */
/* field 'Sym.t' for labels */
/* field 'Sym.r' for labels */
#define LABEL_FORWARD 1 /* label is forward defined */
/* type_decl() types */
@ -305,6 +305,10 @@ int gnu_ext = 1;
/* use Tiny C extensions */
int tcc_ext = 1;
/* max number of callers shown if error */
static int num_callers = 6;
static const char **rt_bound_error_msg;
/* XXX: suppress that ASAP */
static struct TCCState *tcc_state;
@ -572,7 +576,7 @@ int parse_btype(int *type_ptr, AttributeDef *ad);
int type_decl(AttributeDef *ad, int *v, int t, int td);
void error(const char *fmt, ...);
void rt_error(unsigned long pc, const char *fmt, ...);
void rt_error(struct ucontext *uc, const char *fmt, ...);
void vpushi(int v);
void vset(int t, int r, int v);
void type_to_str(char *buf, int buf_size,
@ -910,6 +914,7 @@ static void put_extern_sym(Sym *sym, Section *section,
name = get_tok_str(sym->v, NULL);
#ifdef CONFIG_TCC_BCHECK
if (do_bounds_check) {
/* XXX: avoid doing that for statics ? */
/* if bound checking is activated, we change some function
names by adding the "__bound" prefix */
switch(sym->v) {
@ -5475,8 +5480,7 @@ static void unary(void)
test_lvalue();
vtop->t = mk_pointer(vtop->t);
gaddrof();
} else
if (t == '!') {
} else if (t == '!') {
unary();
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST)
vtop->c.i = !vtop->c.i;
@ -5484,21 +5488,18 @@ static void unary(void)
vtop->c.i = vtop->c.i ^ 1;
else
vset(VT_INT, VT_JMP, gtst(1, 0));
} else
if (t == '~') {
} else if (t == '~') {
unary();
vpushi(-1);
gen_op('^');
} else
if (t == '+') {
} else if (t == '+') {
/* in order to force cast, we add zero */
unary();
if ((vtop->t & VT_BTYPE) == VT_PTR)
error("pointer not accepted for unary plus");
vpushi(0);
gen_op('+');
} else
if (t == TOK_SIZEOF || t == TOK_ALIGNOF) {
} else if (t == TOK_SIZEOF || t == TOK_ALIGNOF) {
if (tok == '(') {
ft = parse_expr_type();
} else {
@ -5509,16 +5510,28 @@ static void unary(void)
vpushi(size);
else
vpushi(align);
} else
if (t == TOK_INC || t == TOK_DEC) {
} else if (t == TOK_INC || t == TOK_DEC) {
unary();
inc(0, t);
} else if (t == '-') {
vpushi(0);
unary();
gen_op('-');
} else
{
} else if (t == TOK_LAND && gnu_ext) {
/* allow to take the address of a label */
if (tok < TOK_UIDENT)
expect("label identifier");
s = sym_find1(&label_stack, tok);
if (!s) {
s = sym_push1(&label_stack, tok, 0, 0);
s->r = LABEL_FORWARD;
}
if (!s->t)
s->t = mk_pointer(VT_VOID) | VT_STATIC;
vset(s->t, VT_CONST | VT_SYM, 0);
vtop->sym = s;
next();
} else {
if (t < TOK_UIDENT)
expect("identifier");
s = sym_find(t);
@ -6200,16 +6213,29 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_re
} else
if (tok == TOK_GOTO) {
next();
s = sym_find1(&label_stack, tok);
/* put forward definition if needed */
if (!s)
s = sym_push1(&label_stack, tok, LABEL_FORWARD, 0);
/* label already defined */
if (s->t & LABEL_FORWARD)
s->c = gjmp(s->c);
else
gjmp_addr(s->c);
next();
if (tok == '*' && gnu_ext) {
/* computed goto */
next();
gexpr();
if ((vtop->t & VT_BTYPE) != VT_PTR)
expect("pointer");
ggoto();
} else if (tok >= TOK_UIDENT) {
s = sym_find1(&label_stack, tok);
/* put forward definition if needed */
if (!s) {
s = sym_push1(&label_stack, tok, 0, 0);
s->r = LABEL_FORWARD;
}
/* label already defined */
if (s->r & LABEL_FORWARD)
s->next = (void *)gjmp((long)s->next);
else
gjmp_addr((long)s->next);
next();
} else {
expect("label identifier");
}
skip(';');
} else {
b = is_label();
@ -6217,14 +6243,14 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_re
/* label case */
s = sym_find1(&label_stack, b);
if (s) {
if (!(s->t & LABEL_FORWARD))
if (!(s->r & LABEL_FORWARD))
error("multiple defined label");
gsym(s->c);
s->c = ind;
s->t = 0;
gsym((long)s->next);
} else {
sym_push1(&label_stack, b, 0, ind);
s = sym_push1(&label_stack, b, 0, 0);
}
s->next = (void *)ind;
s->r = 0;
/* we accept this, but it is a mistake */
if (tok == '}')
warning("deprecated use of label at end of compound statement");
@ -6997,6 +7023,22 @@ static void decl(int l)
gsym(rsym);
gfunc_epilog();
cur_text_section->data_offset = ind;
/* look if any labels are undefined. Define symbols if
'&&label' was used. */
{
Sym *s;
for(s = label_stack.top; s != NULL; s = s->prev) {
if (s->r & LABEL_FORWARD) {
error("label '%s' used but not defined",
get_tok_str(s->v, NULL));
}
if (s->c) {
/* define corresponding symbol. A size of
1 is put. */
put_extern_sym(s, cur_text_section, (long)s->next, 1);
}
}
}
sym_pop(&label_stack, NULL); /* reset label stack */
sym_pop(&local_stack, NULL); /* reset local stack */
/* end of function */
@ -7234,6 +7276,8 @@ static void rt_printline(unsigned long wanted_pc)
int incl_index, len, last_line_num, i;
const char *str, *p;
fprintf(stderr, "0x%08lx:", wanted_pc);
func_name[0] = '\0';
func_addr = 0;
incl_index = 0;
@ -7247,6 +7291,10 @@ static void rt_printline(unsigned long wanted_pc)
/* function start or end */
case N_FUN:
if (sym->n_strx == 0) {
/* we test if between last line and end of function */
pc = sym->n_value + func_addr;
if (wanted_pc >= last_pc && wanted_pc < pc)
goto found;
func_name[0] = '\0';
func_addr = 0;
} else {
@ -7300,31 +7348,98 @@ static void rt_printline(unsigned long wanted_pc)
}
sym++;
}
/* did not find line number info: */
fprintf(stderr, "(no debug info, pc=0x%08lx): ", wanted_pc);
/* second pass: we try symtab symbols (no line number info) */
incl_index = 0;
{
Elf32_Sym *sym, *sym_end;
int type;
sym_end = (Elf32_Sym *)(symtab_section->data + symtab_section->data_offset);
for(sym = (Elf32_Sym *)symtab_section->data + 1;
sym < sym_end;
sym++) {
type = ELF32_ST_TYPE(sym->st_info);
if (type == STT_FUNC) {
if (wanted_pc >= sym->st_value &&
wanted_pc < sym->st_value + sym->st_size) {
pstrcpy(last_func_name, sizeof(last_func_name),
strtab_section->data + sym->st_name);
goto found;
}
}
}
}
/* did not find any info: */
fprintf(stderr, " ???\n");
return;
found:
for(i = 0; i < incl_index - 1; i++)
fprintf(stderr, "In file included from %s\n",
incl_files[i]);
if (incl_index > 0) {
fprintf(stderr, "%s:%d: ",
incl_files[incl_index - 1], last_line_num);
}
if (last_func_name[0] != '\0') {
fprintf(stderr, "in function '%s()': ", last_func_name);
fprintf(stderr, " %s()", last_func_name);
}
if (incl_index > 0) {
fprintf(stderr, " (%s:%d",
incl_files[incl_index - 1], last_line_num);
for(i = incl_index - 2; i >= 0; i--)
fprintf(stderr, ", included from %s", incl_files[i]);
fprintf(stderr, ")");
}
fprintf(stderr, "\n");
}
#ifdef __i386__
#ifndef EIP
#define EIP 14
#define EBP 6
#endif
/* return the PC at frame level 'level'. Return non zero if not found */
static int rt_get_caller_pc(unsigned long *paddr,
struct ucontext *uc, int level)
{
unsigned long fp;
int i;
if (level == 0) {
*paddr = uc->uc_mcontext.gregs[EIP];
return 0;
} else {
fp = uc->uc_mcontext.gregs[EBP];
for(i=1;i<level;i++) {
/* XXX: check address validity with program info */
if (fp <= 0x1000 || fp >= 0xc0000000)
return -1;
fp = ((unsigned long *)fp)[0];
}
*paddr = ((unsigned long *)fp)[1];
return 0;
}
}
#else
#error add corresponding function
#endif
/* emit a run time error at position 'pc' */
void rt_error(unsigned long pc, const char *fmt, ...)
void rt_error(struct ucontext *uc, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
unsigned long pc;
int i;
rt_printline(pc);
va_start(ap, fmt);
fprintf(stderr, "Runtime error: ");
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
for(i=0;i<num_callers;i++) {
if (rt_get_caller_pc(&pc, uc, i) < 0)
break;
if (i == 0)
fprintf(stderr, "at ");
else
fprintf(stderr, "by ");
rt_printline(pc);
}
exit(255);
va_end(ap);
}
@ -7334,38 +7449,34 @@ void rt_error(unsigned long pc, const char *fmt, ...)
static void sig_error(int signum, siginfo_t *siginf, void *puc)
{
struct ucontext *uc = puc;
unsigned long pc;
#ifdef __i386__
pc = uc->uc_mcontext.gregs[14];
#else
#error please put the right sigcontext field
#endif
switch(signum) {
case SIGFPE:
switch(siginf->si_code) {
case FPE_INTDIV:
case FPE_FLTDIV:
rt_error(pc, "division by zero");
rt_error(uc, "division by zero");
break;
default:
rt_error(pc, "floating point exception");
rt_error(uc, "floating point exception");
break;
}
break;
case SIGBUS:
case SIGSEGV:
rt_error(pc, "dereferencing invalid pointer");
if (rt_bound_error_msg && *rt_bound_error_msg)
rt_error(uc, *rt_bound_error_msg);
else
rt_error(uc, "dereferencing invalid pointer");
break;
case SIGILL:
rt_error(pc, "illegal instruction");
rt_error(uc, "illegal instruction");
break;
case SIGABRT:
rt_error(pc, "abort() called");
rt_error(uc, "abort() called");
break;
default:
rt_error(pc, "caught signal %d", signum);
rt_error(uc, "caught signal %d", signum);
break;
}
exit(255);
@ -7440,11 +7551,9 @@ int tcc_run(TCCState *s1, int argc, char **argv)
#ifdef CONFIG_TCC_BCHECK
if (do_bounds_check) {
void (*bound_init)(void);
void **bound_error_func;
/* set error function */
bound_error_func = (void **)tcc_get_symbol(s1, "__bound_error_func");
*bound_error_func = rt_error;
rt_bound_error_msg = (void *)tcc_get_symbol(s1, "__bound_error_msg");
/* XXX: use .init section so that it also work in binary ? */
bound_init = (void *)tcc_get_symbol(s1, "__bound_init");
@ -7782,9 +7891,9 @@ int tcc_set_output_type(TCCState *s, int output_type)
void help(void)
{
printf("tcc version 0.9.12 - Tiny C Compiler - Copyright (C) 2001, 2002 Fabrice Bellard\n"
printf("tcc version 0.9.13 - Tiny C Compiler - Copyright (C) 2001, 2002 Fabrice Bellard\n"
"usage: tcc [-c] [-o outfile] [-Bdir] [-bench] [-Idir] [-Dsym[=val]] [-Usym]\n"
" [-g] [-b] [-Ldir] [-llib] [-shared] [-static]\n"
" [-g] [-b] [-bt N] [-Ldir] [-llib] [-shared] [-static]\n"
" [--] infile1 [infile2... --] [infile_args...]\n"
"\n"
"General options:\n"
@ -7798,17 +7907,18 @@ void help(void)
" -Idir add include path 'dir'\n"
" -Dsym[=val] define 'sym' with value 'val'\n"
" -Usym undefine 'sym'\n"
"C compiler options:\n"
" -g generate runtime debug info\n"
#ifdef CONFIG_TCC_BCHECK
" -b compile with built-in memory and bounds checker (implies -g)\n"
#endif
"Linker options:\n"
" -Ldir add library path 'dir'\n"
" -llib link with dynamic or static library 'lib'\n"
" -shared generate a shared library\n"
" -static static linking\n"
" -r relocatable output\n"
"Debugger options:\n"
" -g generate runtime debug info\n"
#ifdef CONFIG_TCC_BCHECK
" -b compile with built-in memory and bounds checker (implies -g)\n"
#endif
" -bt N show N callers in stack traces\n"
);
}
@ -7884,7 +7994,9 @@ int main(int argc, char **argv)
nb_libraries++;
} else if (!strcmp(r + 1, "bench")) {
do_bench = 1;
} else
} else if (!strcmp(r + 1, "bt")) {
num_callers = atoi(argv[optind++]);
} else
#ifdef CONFIG_TCC_BCHECK
if (r[1] == 'b') {
do_bounds_check = 1;
@ -7982,7 +8094,9 @@ int main(int argc, char **argv)
ret = tcc_run(s, argc - optind, argv + optind);
}
the_end:
tcc_delete(s);
/* XXX: cannot do it with bound checking because of the malloc hooks */
if (!do_bounds_check)
tcc_delete(s);
#ifdef MEM_DEBUG
if (do_bench) {