forked from Mirrors/tinycc
added minimal debug info support
parent
60f781c443
commit
4124f1a1c1
578
tcc.c
578
tcc.c
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* TCC - Tiny C Compiler
|
* TCC - Tiny C Compiler
|
||||||
*
|
*
|
||||||
* Copyright (c) 2001 Fabrice Bellard
|
* Copyright (c) 2001, 2002 Fabrice Bellard
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -23,7 +23,12 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/ucontext.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <elf.h>
|
||||||
|
#include <stab.h>
|
||||||
#ifndef CONFIG_TCC_STATIC
|
#ifndef CONFIG_TCC_STATIC
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -109,6 +114,11 @@ typedef struct Section {
|
||||||
char name[64]; /* section name */
|
char name[64]; /* section name */
|
||||||
unsigned char *data; /* section data */
|
unsigned char *data; /* section data */
|
||||||
unsigned char *data_ptr; /* current data pointer */
|
unsigned char *data_ptr; /* current data pointer */
|
||||||
|
int sh_num; /* elf section number */
|
||||||
|
int sh_type; /* elf section type */
|
||||||
|
int sh_flags; /* elf section flags */
|
||||||
|
int sh_entsize; /* elf entry size */
|
||||||
|
struct Section *link; /* link to another section */
|
||||||
struct Section *next;
|
struct Section *next;
|
||||||
} Section;
|
} Section;
|
||||||
|
|
||||||
|
@ -148,9 +158,13 @@ CValue tokc, tok1c;
|
||||||
|
|
||||||
/* sections */
|
/* sections */
|
||||||
Section *first_section;
|
Section *first_section;
|
||||||
|
int section_num;
|
||||||
Section *text_section, *data_section, *bss_section; /* predefined sections */
|
Section *text_section, *data_section, *bss_section; /* predefined sections */
|
||||||
Section *cur_text_section; /* current section where function code is
|
Section *cur_text_section; /* current section where function code is
|
||||||
generated */
|
generated */
|
||||||
|
Section *bounds_section; /* contains global data bound description */
|
||||||
|
/* debug sections */
|
||||||
|
Section *stab_section, *stabstr_section, *symtab_section, *strtab_section;
|
||||||
|
|
||||||
/* loc : local variable index
|
/* loc : local variable index
|
||||||
ind : output code index
|
ind : output code index
|
||||||
|
@ -163,6 +177,7 @@ int global_expr; /* true if compound literals must be allocated
|
||||||
globally (used during initializers parsing */
|
globally (used during initializers parsing */
|
||||||
int func_vt, func_vc; /* current function return type (used by
|
int func_vt, func_vc; /* current function return type (used by
|
||||||
return instruction) */
|
return instruction) */
|
||||||
|
int last_line_num, last_ind, func_ind; /* debug last line number and pc */
|
||||||
int tok_ident;
|
int tok_ident;
|
||||||
TokenSym **table_ident;
|
TokenSym **table_ident;
|
||||||
TokenSym *hash_ident[TOK_HASH_SIZE];
|
TokenSym *hash_ident[TOK_HASH_SIZE];
|
||||||
|
@ -178,6 +193,13 @@ IncludeFile include_stack[INCLUDE_STACK_SIZE], *include_stack_ptr;
|
||||||
int ifdef_stack[IFDEF_STACK_SIZE], *ifdef_stack_ptr;
|
int ifdef_stack[IFDEF_STACK_SIZE], *ifdef_stack_ptr;
|
||||||
char *include_paths[INCLUDE_PATHS_MAX];
|
char *include_paths[INCLUDE_PATHS_MAX];
|
||||||
int nb_include_paths;
|
int nb_include_paths;
|
||||||
|
int char_pointer_type;
|
||||||
|
|
||||||
|
/* compile with debug symbol (and use them if error during execution) */
|
||||||
|
int do_debug = 0;
|
||||||
|
|
||||||
|
/* compile with built-in memory and bounds checker */
|
||||||
|
int do_bounds_check = 0;
|
||||||
|
|
||||||
/* use GNU C extensions */
|
/* use GNU C extensions */
|
||||||
int gnu_ext = 1;
|
int gnu_ext = 1;
|
||||||
|
@ -380,6 +402,7 @@ void decl_initializer(int t, int r, int c, int first, int size_only);
|
||||||
int decl_initializer_alloc(int t, AttributeDef *ad, int r, int has_init);
|
int decl_initializer_alloc(int t, AttributeDef *ad, int r, int has_init);
|
||||||
int gv(int rc);
|
int gv(int rc);
|
||||||
void move_reg(int r, int s);
|
void move_reg(int r, int s);
|
||||||
|
void save_regs(void);
|
||||||
void save_reg(int r);
|
void save_reg(int r);
|
||||||
void vpop(void);
|
void vpop(void);
|
||||||
void vswap(void);
|
void vswap(void);
|
||||||
|
@ -407,10 +430,19 @@ int type_decl(int *v, int t, int td);
|
||||||
void error(const char *fmt, ...);
|
void error(const char *fmt, ...);
|
||||||
void vpushi(int v);
|
void vpushi(int v);
|
||||||
void vset(int t, int r, int v);
|
void vset(int t, int r, int v);
|
||||||
void greloc(Sym *s, int addr, int type);
|
|
||||||
void type_to_str(char *buf, int buf_size,
|
void type_to_str(char *buf, int buf_size,
|
||||||
int t, const char *varstr);
|
int t, const char *varstr);
|
||||||
|
|
||||||
|
/* section generation */
|
||||||
|
void greloc(Sym *s, int addr, int type);
|
||||||
|
static int put_elf_str(Section *s, const char *sym);
|
||||||
|
static void put_elf_sym(Section *s,
|
||||||
|
unsigned long value, unsigned long size,
|
||||||
|
int info, int other, int shndx, const char *name);
|
||||||
|
static void put_stabs(const char *str, int type, int other, int desc, int value);
|
||||||
|
static void put_stabn(int type, int other, int desc, int value);
|
||||||
|
static void put_stabd(int type, int other, int desc);
|
||||||
|
|
||||||
/* true if float/double/long double type */
|
/* true if float/double/long double type */
|
||||||
static inline int is_float(int t)
|
static inline int is_float(int t)
|
||||||
{
|
{
|
||||||
|
@ -419,6 +451,8 @@ static inline int is_float(int t)
|
||||||
return bt == VT_LDOUBLE || bt == VT_DOUBLE || bt == VT_FLOAT;
|
return bt == VT_LDOUBLE || bt == VT_DOUBLE || bt == VT_FLOAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "bcheck.c"
|
||||||
|
|
||||||
#include "i386-gen.c"
|
#include "i386-gen.c"
|
||||||
|
|
||||||
#ifdef CONFIG_TCC_STATIC
|
#ifdef CONFIG_TCC_STATIC
|
||||||
|
@ -571,7 +605,7 @@ char *pstrcat(char *buf, int buf_size, const char *s)
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
Section *new_section(const char *name)
|
Section *new_section(const char *name, int sh_type, int sh_flags)
|
||||||
{
|
{
|
||||||
Section *sec, **psec;
|
Section *sec, **psec;
|
||||||
void *data;
|
void *data;
|
||||||
|
@ -579,7 +613,12 @@ Section *new_section(const char *name)
|
||||||
sec = malloc(sizeof(Section));
|
sec = malloc(sizeof(Section));
|
||||||
if (!sec)
|
if (!sec)
|
||||||
error("memory full");
|
error("memory full");
|
||||||
|
memset(sec, 0, sizeof(Section));
|
||||||
pstrcpy(sec->name, sizeof(sec->name), name);
|
pstrcpy(sec->name, sizeof(sec->name), name);
|
||||||
|
sec->link = NULL;
|
||||||
|
sec->sh_num = ++section_num;
|
||||||
|
sec->sh_type = sh_type;
|
||||||
|
sec->sh_flags = sh_flags;
|
||||||
data = mmap(NULL, SECTION_VSIZE,
|
data = mmap(NULL, SECTION_VSIZE,
|
||||||
PROT_EXEC | PROT_READ | PROT_WRITE,
|
PROT_EXEC | PROT_READ | PROT_WRITE,
|
||||||
MAP_PRIVATE | MAP_ANONYMOUS,
|
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||||
|
@ -606,7 +645,8 @@ Section *find_section(const char *name)
|
||||||
if (!strcmp(name, sec->name))
|
if (!strcmp(name, sec->name))
|
||||||
return sec;
|
return sec;
|
||||||
}
|
}
|
||||||
return new_section(name);
|
/* sections are created as PROGBITS */
|
||||||
|
return new_section(name, SHT_PROGBITS, SHF_ALLOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add a new relocation entry to symbol 's' */
|
/* add a new relocation entry to symbol 's' */
|
||||||
|
@ -936,6 +976,10 @@ int handle_eof(void)
|
||||||
{
|
{
|
||||||
if (include_stack_ptr == include_stack)
|
if (include_stack_ptr == include_stack)
|
||||||
return -1;
|
return -1;
|
||||||
|
/* add end of include file debug info */
|
||||||
|
if (do_debug) {
|
||||||
|
put_stabd(N_EINCL, 0, 0);
|
||||||
|
}
|
||||||
/* pop include stack */
|
/* pop include stack */
|
||||||
fclose(file);
|
fclose(file);
|
||||||
free(filename);
|
free(filename);
|
||||||
|
@ -1288,6 +1332,10 @@ void preprocess(void)
|
||||||
file = f;
|
file = f;
|
||||||
filename = strdup(buf1);
|
filename = strdup(buf1);
|
||||||
line_num = 1;
|
line_num = 1;
|
||||||
|
/* add include file debug info */
|
||||||
|
if (do_debug) {
|
||||||
|
put_stabs(filename, N_BINCL, 0, 0, 0);
|
||||||
|
}
|
||||||
} else if (tok == TOK_IFNDEF) {
|
} else if (tok == TOK_IFNDEF) {
|
||||||
c = 1;
|
c = 1;
|
||||||
goto do_ifdef;
|
goto do_ifdef;
|
||||||
|
@ -2831,7 +2879,17 @@ void gen_op(int op)
|
||||||
/* XXX: cast to int ? (long long case) */
|
/* XXX: cast to int ? (long long case) */
|
||||||
vpushi(pointed_size(vtop[-1].t));
|
vpushi(pointed_size(vtop[-1].t));
|
||||||
gen_op('*');
|
gen_op('*');
|
||||||
|
if (do_bounds_check) {
|
||||||
|
/* if bounded pointers, we generate a special code to test bounds */
|
||||||
|
if (op == '-') {
|
||||||
|
vpushi(0);
|
||||||
|
vswap();
|
||||||
|
gen_op('-');
|
||||||
|
}
|
||||||
|
gen_bounded_ptr_add();
|
||||||
|
} else {
|
||||||
gen_opc(op);
|
gen_opc(op);
|
||||||
|
}
|
||||||
/* put again type if gen_opc() swaped operands */
|
/* put again type if gen_opc() swaped operands */
|
||||||
vtop->t = t1;
|
vtop->t = t1;
|
||||||
}
|
}
|
||||||
|
@ -4184,7 +4242,7 @@ void unary(void)
|
||||||
if (!s)
|
if (!s)
|
||||||
error("field not found");
|
error("field not found");
|
||||||
/* add field offset to pointer */
|
/* add field offset to pointer */
|
||||||
vtop->t = VT_INT; /* change type to int */
|
vtop->t = char_pointer_type; /* change type to 'char *' */
|
||||||
vpushi(s->c);
|
vpushi(s->c);
|
||||||
gen_op('+');
|
gen_op('+');
|
||||||
/* change type to field type, and set to lvalue */
|
/* change type to field type, and set to lvalue */
|
||||||
|
@ -4524,6 +4582,14 @@ void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_reg)
|
||||||
int a, b, c, d;
|
int a, b, c, d;
|
||||||
Sym *s;
|
Sym *s;
|
||||||
|
|
||||||
|
/* generate line number info */
|
||||||
|
if (do_debug &&
|
||||||
|
(last_line_num != line_num || last_ind != ind)) {
|
||||||
|
put_stabn(N_SLINE, 0, line_num, ind - func_ind);
|
||||||
|
last_ind = ind;
|
||||||
|
last_line_num = line_num;
|
||||||
|
}
|
||||||
|
|
||||||
if (tok == TOK_IF) {
|
if (tok == TOK_IF) {
|
||||||
/* if test */
|
/* if test */
|
||||||
next();
|
next();
|
||||||
|
@ -5138,6 +5204,17 @@ int decl_initializer_alloc(int t, AttributeDef *ad, int r, int has_init)
|
||||||
initializers themselves can create new
|
initializers themselves can create new
|
||||||
initializers */
|
initializers */
|
||||||
data_offset += size;
|
data_offset += size;
|
||||||
|
/* handles bounds */
|
||||||
|
if (do_bounds_check) {
|
||||||
|
int *bounds_ptr;
|
||||||
|
/* first, we need to add at least one byte between each region */
|
||||||
|
data_offset++;
|
||||||
|
/* then add global data info */
|
||||||
|
bounds_ptr = (int *)bounds_section->data_ptr;
|
||||||
|
*bounds_ptr++ = addr;
|
||||||
|
*bounds_ptr++ = size;
|
||||||
|
bounds_section->data_ptr = (unsigned char *)bounds_ptr;
|
||||||
|
}
|
||||||
sec->data_ptr = (unsigned char *)data_offset;
|
sec->data_ptr = (unsigned char *)data_offset;
|
||||||
}
|
}
|
||||||
if (has_init) {
|
if (has_init) {
|
||||||
|
@ -5152,6 +5229,28 @@ int decl_initializer_alloc(int t, AttributeDef *ad, int r, int has_init)
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void put_func_debug(int t)
|
||||||
|
{
|
||||||
|
int bind;
|
||||||
|
char buf[512];
|
||||||
|
|
||||||
|
if (t & VT_STATIC)
|
||||||
|
bind = STB_LOCAL;
|
||||||
|
else
|
||||||
|
bind = STB_GLOBAL;
|
||||||
|
put_elf_sym(symtab_section, ind, 0,
|
||||||
|
ELF32_ST_INFO(bind, STT_FUNC), 0,
|
||||||
|
cur_text_section->sh_num, funcname);
|
||||||
|
/* stabs info */
|
||||||
|
/* XXX: we put here a dummy type */
|
||||||
|
snprintf(buf, sizeof(buf), "%s:%c1",
|
||||||
|
funcname, t & VT_STATIC ? 'f' : 'F');
|
||||||
|
put_stabs(buf, N_FUN, 0, line_num, ind);
|
||||||
|
func_ind = ind;
|
||||||
|
last_ind = 0;
|
||||||
|
last_line_num = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* 'l' is VT_LOCAL or VT_CONST to define default storage type */
|
/* 'l' is VT_LOCAL or VT_CONST to define default storage type */
|
||||||
void decl(int l)
|
void decl(int l)
|
||||||
{
|
{
|
||||||
|
@ -5213,6 +5312,9 @@ void decl(int l)
|
||||||
}
|
}
|
||||||
sym->r = VT_CONST;
|
sym->r = VT_CONST;
|
||||||
funcname = get_tok_str(v, NULL);
|
funcname = get_tok_str(v, NULL);
|
||||||
|
/* put debug symbol */
|
||||||
|
if (do_debug)
|
||||||
|
put_func_debug(t);
|
||||||
/* push a dummy symbol to enable local sym storage */
|
/* push a dummy symbol to enable local sym storage */
|
||||||
sym_push1(&local_stack, 0, 0, 0);
|
sym_push1(&local_stack, 0, 0, 0);
|
||||||
gfunc_prolog(t);
|
gfunc_prolog(t);
|
||||||
|
@ -5224,6 +5326,10 @@ void decl(int l)
|
||||||
cur_text_section->data_ptr = (unsigned char *)ind;
|
cur_text_section->data_ptr = (unsigned char *)ind;
|
||||||
sym_pop(&label_stack, NULL); /* reset label stack */
|
sym_pop(&label_stack, NULL); /* reset label stack */
|
||||||
sym_pop(&local_stack, NULL); /* reset local stack */
|
sym_pop(&local_stack, NULL); /* reset local stack */
|
||||||
|
/* end of function */
|
||||||
|
if (do_debug) {
|
||||||
|
put_stabn(N_FUN, 0, 0, ind - func_ind);
|
||||||
|
}
|
||||||
funcname = ""; /* for safety */
|
funcname = ""; /* for safety */
|
||||||
func_vt = VT_VOID; /* for safety */
|
func_vt = VT_VOID; /* for safety */
|
||||||
break;
|
break;
|
||||||
|
@ -5330,6 +5436,7 @@ void resolve_global_syms(void)
|
||||||
int tcc_compile_file(const char *filename1)
|
int tcc_compile_file(const char *filename1)
|
||||||
{
|
{
|
||||||
Sym *define_start;
|
Sym *define_start;
|
||||||
|
char buf[512];
|
||||||
|
|
||||||
line_num = 1;
|
line_num = 1;
|
||||||
funcname = "";
|
funcname = "";
|
||||||
|
@ -5344,6 +5451,17 @@ int tcc_compile_file(const char *filename1)
|
||||||
vtop = vstack - 1;
|
vtop = vstack - 1;
|
||||||
anon_sym = SYM_FIRST_ANOM;
|
anon_sym = SYM_FIRST_ANOM;
|
||||||
|
|
||||||
|
/* file info: full path + filename */
|
||||||
|
if (do_debug) {
|
||||||
|
getcwd(buf, sizeof(buf));
|
||||||
|
pstrcat(buf, sizeof(buf), "/");
|
||||||
|
put_stabs(buf, N_SO, 0, 0, (unsigned long)text_section->data_ptr);
|
||||||
|
put_stabs(filename, N_SO, 0, 0, (unsigned long)text_section->data_ptr);
|
||||||
|
}
|
||||||
|
/* define common 'char *' type because it is often used internally
|
||||||
|
for arrays and struct dereference */
|
||||||
|
char_pointer_type = mk_pointer(VT_BYTE);
|
||||||
|
|
||||||
define_start = define_stack.top;
|
define_start = define_stack.top;
|
||||||
inp();
|
inp();
|
||||||
ch = '\n'; /* needed to parse correctly first preprocessor command */
|
ch = '\n'; /* needed to parse correctly first preprocessor command */
|
||||||
|
@ -5353,6 +5471,11 @@ int tcc_compile_file(const char *filename1)
|
||||||
expect("declaration");
|
expect("declaration");
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
|
/* end of translation unit info */
|
||||||
|
if (do_debug) {
|
||||||
|
put_stabn(N_SO, 0, 0, (unsigned long)text_section->data_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
/* reset define stack, but leave -Dsymbols (may be incorrect if
|
/* reset define stack, but leave -Dsymbols (may be incorrect if
|
||||||
they are undefined) */
|
they are undefined) */
|
||||||
sym_pop(&define_stack, define_start);
|
sym_pop(&define_stack, define_start);
|
||||||
|
@ -5429,6 +5552,17 @@ void open_dll(char *libname)
|
||||||
error((char *)dlerror());
|
error((char *)dlerror());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void *resolve_sym(const char *sym)
|
||||||
|
{
|
||||||
|
void *ptr;
|
||||||
|
if (do_bounds_check) {
|
||||||
|
ptr = bound_resolve_sym(sym);
|
||||||
|
if (ptr)
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
return dlsym(NULL, sym);
|
||||||
|
}
|
||||||
|
|
||||||
void resolve_extern_syms(void)
|
void resolve_extern_syms(void)
|
||||||
{
|
{
|
||||||
Sym *s, *s1;
|
Sym *s, *s1;
|
||||||
|
@ -5443,7 +5577,7 @@ void resolve_extern_syms(void)
|
||||||
and patch it */
|
and patch it */
|
||||||
if (s->c) {
|
if (s->c) {
|
||||||
str = get_tok_str(s->v, NULL);
|
str = get_tok_str(s->v, NULL);
|
||||||
addr = (int)dlsym(NULL, str);
|
addr = (int)resolve_sym(str);
|
||||||
if (!addr)
|
if (!addr)
|
||||||
error("unresolved external reference '%s'", str);
|
error("unresolved external reference '%s'", str);
|
||||||
greloc_patch(s, addr);
|
greloc_patch(s, addr);
|
||||||
|
@ -5453,25 +5587,403 @@ void resolve_extern_syms(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* output a binary file (for testing) */
|
static int put_elf_str(Section *s, const char *sym)
|
||||||
|
{
|
||||||
|
int c, offset;
|
||||||
|
offset = s->data_ptr - s->data;
|
||||||
|
for(;;) {
|
||||||
|
c = *sym++;
|
||||||
|
*s->data_ptr++ = c;
|
||||||
|
if (c == '\0')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_elf_sym(Section *s,
|
||||||
|
unsigned long value, unsigned long size,
|
||||||
|
int info, int other, int shndx, const char *name)
|
||||||
|
{
|
||||||
|
int name_offset;
|
||||||
|
Elf32_Sym *sym;
|
||||||
|
|
||||||
|
sym = (Elf32_Sym *)s->data_ptr;
|
||||||
|
if (name)
|
||||||
|
name_offset = put_elf_str(s->link, name);
|
||||||
|
else
|
||||||
|
name_offset = 0;
|
||||||
|
sym->st_name = name_offset;
|
||||||
|
sym->st_value = value;
|
||||||
|
sym->st_size = size;
|
||||||
|
sym->st_info = info;
|
||||||
|
sym->st_other = other;
|
||||||
|
sym->st_shndx = shndx;
|
||||||
|
s->data_ptr += sizeof(Elf32_Sym);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* put stab debug information */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned long n_strx; /* index into string table of name */
|
||||||
|
unsigned char n_type; /* type of symbol */
|
||||||
|
unsigned char n_other; /* misc info (usually empty) */
|
||||||
|
unsigned short n_desc; /* description field */
|
||||||
|
unsigned long n_value; /* value of symbol */
|
||||||
|
} Stab_Sym;
|
||||||
|
|
||||||
|
static void put_stabs(const char *str, int type, int other, int desc, int value)
|
||||||
|
{
|
||||||
|
Stab_Sym *sym;
|
||||||
|
|
||||||
|
sym = (Stab_Sym *)stab_section->data_ptr;
|
||||||
|
if (str) {
|
||||||
|
sym->n_strx = put_elf_str(stabstr_section, str);
|
||||||
|
} else {
|
||||||
|
sym->n_strx = 0;
|
||||||
|
}
|
||||||
|
sym->n_type = type;
|
||||||
|
sym->n_other = other;
|
||||||
|
sym->n_desc = desc;
|
||||||
|
sym->n_value = value;
|
||||||
|
|
||||||
|
stab_section->data_ptr += sizeof(Stab_Sym);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_stabn(int type, int other, int desc, int value)
|
||||||
|
{
|
||||||
|
put_stabs(NULL, type, other, desc, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_stabd(int type, int other, int desc)
|
||||||
|
{
|
||||||
|
put_stabs(NULL, type, other, desc, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* output an ELF file (currently, only for testing) */
|
||||||
void build_exe(char *filename)
|
void build_exe(char *filename)
|
||||||
{
|
{
|
||||||
|
Elf32_Ehdr ehdr;
|
||||||
FILE *f;
|
FILE *f;
|
||||||
|
int shnum, i, phnum, file_offset, offset, size, j;
|
||||||
|
Section *sec, *strsec;
|
||||||
|
Elf32_Shdr *shdr, *sh;
|
||||||
|
Elf32_Phdr *phdr, *ph;
|
||||||
|
|
||||||
|
memset(&ehdr, 0, sizeof(ehdr));
|
||||||
|
|
||||||
|
/* we add a section for symbols */
|
||||||
|
strsec = new_section(".shstrtab", SHT_STRTAB, 0);
|
||||||
|
put_elf_str(strsec, "");
|
||||||
|
|
||||||
|
/* count number of sections and compute number of program segments */
|
||||||
|
shnum = 1; /* section index zero is reserved */
|
||||||
|
phnum = 0;
|
||||||
|
for(sec = first_section; sec != NULL; sec = sec->next) {
|
||||||
|
shnum++;
|
||||||
|
if (sec->sh_flags & SHF_ALLOC)
|
||||||
|
phnum++;
|
||||||
|
}
|
||||||
|
/* allocate section headers */
|
||||||
|
shdr = malloc(shnum * sizeof(Elf32_Shdr));
|
||||||
|
if (!shdr)
|
||||||
|
error("memory full");
|
||||||
|
memset(shdr, 0, shnum * sizeof(Elf32_Shdr));
|
||||||
|
/* allocate program segment headers */
|
||||||
|
phdr = malloc(phnum * sizeof(Elf32_Phdr));
|
||||||
|
if (!phdr)
|
||||||
|
error("memory full");
|
||||||
|
memset(phdr, 0, phnum * sizeof(Elf32_Phdr));
|
||||||
|
|
||||||
|
/* XXX: find correct load order */
|
||||||
|
file_offset = sizeof(Elf32_Ehdr) + phnum * sizeof(Elf32_Phdr);
|
||||||
|
for(sec = first_section, i = 1; sec != NULL; sec = sec->next, i++) {
|
||||||
|
sh = &shdr[i];
|
||||||
|
sh->sh_name = put_elf_str(strsec, sec->name);
|
||||||
|
sh->sh_type = sec->sh_type;
|
||||||
|
sh->sh_flags = sec->sh_flags;
|
||||||
|
sh->sh_entsize = sec->sh_entsize;
|
||||||
|
if (sec->link)
|
||||||
|
sh->sh_link = sec->link->sh_num;
|
||||||
|
if (sh->sh_type == SHT_STRTAB) {
|
||||||
|
sh->sh_addralign = 1;
|
||||||
|
} else if (sh->sh_type == SHT_SYMTAB ||
|
||||||
|
(sh->sh_flags & SHF_ALLOC) == 0) {
|
||||||
|
sh->sh_addralign = 4;
|
||||||
|
} else {
|
||||||
|
sh->sh_addr = (Elf32_Word)sec->data;
|
||||||
|
sh->sh_addralign = 4096;
|
||||||
|
}
|
||||||
|
sh->sh_size = (Elf32_Word)sec->data_ptr - (Elf32_Word)sec->data;
|
||||||
|
/* align to section start */
|
||||||
|
file_offset = (file_offset + sh->sh_addralign - 1) &
|
||||||
|
~(sh->sh_addralign - 1);
|
||||||
|
sh->sh_offset = file_offset;
|
||||||
|
file_offset += sh->sh_size;
|
||||||
|
}
|
||||||
|
/* build program headers (simplistic - not fully correct) */
|
||||||
|
j = 0;
|
||||||
|
for(i=1;i<shnum;i++) {
|
||||||
|
sh = &shdr[i];
|
||||||
|
if (sh->sh_type == SHT_PROGBITS &&
|
||||||
|
(sh->sh_flags & SHF_ALLOC) != 0) {
|
||||||
|
ph = &phdr[j++];
|
||||||
|
ph->p_type = PT_LOAD;
|
||||||
|
ph->p_offset = sh->sh_offset;
|
||||||
|
ph->p_vaddr = sh->sh_addr;
|
||||||
|
ph->p_paddr = ph->p_vaddr;
|
||||||
|
ph->p_filesz = sh->sh_size;
|
||||||
|
ph->p_memsz = sh->sh_size;
|
||||||
|
ph->p_flags = PF_R;
|
||||||
|
if (sh->sh_flags & SHF_WRITE)
|
||||||
|
ph->p_flags |= PF_W;
|
||||||
|
if (sh->sh_flags & SHF_EXECINSTR)
|
||||||
|
ph->p_flags |= PF_X;
|
||||||
|
ph->p_align = sh->sh_addralign;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* align to 4 */
|
||||||
|
file_offset = (file_offset + 3) & -4;
|
||||||
|
|
||||||
|
/* fill header */
|
||||||
|
ehdr.e_ident[0] = ELFMAG0;
|
||||||
|
ehdr.e_ident[1] = ELFMAG1;
|
||||||
|
ehdr.e_ident[2] = ELFMAG2;
|
||||||
|
ehdr.e_ident[3] = ELFMAG3;
|
||||||
|
ehdr.e_ident[4] = ELFCLASS32;
|
||||||
|
ehdr.e_ident[5] = ELFDATA2LSB;
|
||||||
|
ehdr.e_ident[6] = EV_CURRENT;
|
||||||
|
ehdr.e_type = ET_EXEC;
|
||||||
|
ehdr.e_machine = EM_386;
|
||||||
|
ehdr.e_version = EV_CURRENT;
|
||||||
|
ehdr.e_entry = 0; /* XXX: patch it */
|
||||||
|
ehdr.e_phoff = sizeof(Elf32_Ehdr);
|
||||||
|
ehdr.e_shoff = file_offset;
|
||||||
|
ehdr.e_ehsize = sizeof(Elf32_Ehdr);
|
||||||
|
ehdr.e_phentsize = sizeof(Elf32_Phdr);
|
||||||
|
ehdr.e_phnum = phnum;
|
||||||
|
ehdr.e_shentsize = sizeof(Elf32_Shdr);
|
||||||
|
ehdr.e_shnum = shnum;
|
||||||
|
ehdr.e_shstrndx = shnum - 1;
|
||||||
|
|
||||||
|
/* write elf file */
|
||||||
f = fopen(filename, "w");
|
f = fopen(filename, "w");
|
||||||
fwrite(text_section->data, 1, text_section->data_ptr - text_section->data, f);
|
if (!f)
|
||||||
|
error("could not write '%s'", filename);
|
||||||
|
fwrite(&ehdr, 1, sizeof(Elf32_Ehdr), f);
|
||||||
|
fwrite(phdr, 1, phnum * sizeof(Elf32_Phdr), f);
|
||||||
|
offset = sizeof(Elf32_Ehdr) + phnum * sizeof(Elf32_Phdr);
|
||||||
|
for(sec = first_section, i = 1; sec != NULL; sec = sec->next, i++) {
|
||||||
|
sh = &shdr[i];
|
||||||
|
while (offset < sh->sh_offset) {
|
||||||
|
fputc(0, f);
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
size = sec->data_ptr - sec->data;
|
||||||
|
fwrite(sec->data, 1, size, f);
|
||||||
|
offset += size;
|
||||||
|
}
|
||||||
|
while (offset < ehdr.e_shoff) {
|
||||||
|
fputc(0, f);
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
fwrite(shdr, 1, shnum * sizeof(Elf32_Shdr), f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* print the position in the source file of PC value 'pc' by reading
|
||||||
|
the stabs debug information */
|
||||||
|
static void rt_printline(unsigned long wanted_pc)
|
||||||
|
{
|
||||||
|
Stab_Sym *sym, *sym_end;
|
||||||
|
char func_name[128];
|
||||||
|
unsigned long func_addr, last_pc, pc;
|
||||||
|
const char *incl_files[INCLUDE_STACK_SIZE];
|
||||||
|
int incl_index, len, last_line_num, i;
|
||||||
|
const char *str, *p;
|
||||||
|
|
||||||
|
func_name[0] = '\0';
|
||||||
|
func_addr = 0;
|
||||||
|
incl_index = 0;
|
||||||
|
last_pc = 0xffffffff;
|
||||||
|
last_line_num = 1;
|
||||||
|
sym = (Stab_Sym *)stab_section->data + 1;
|
||||||
|
sym_end = (Stab_Sym *)stab_section->data_ptr;
|
||||||
|
while (sym < sym_end) {
|
||||||
|
switch(sym->n_type) {
|
||||||
|
/* function start or end */
|
||||||
|
case N_FUN:
|
||||||
|
if (sym->n_strx == 0) {
|
||||||
|
func_name[0] = '\0';
|
||||||
|
func_addr = 0;
|
||||||
|
} else {
|
||||||
|
str = stabstr_section->data + sym->n_strx;
|
||||||
|
p = strchr(str, ':');
|
||||||
|
if (!p) {
|
||||||
|
pstrcpy(func_name, sizeof(func_name), str);
|
||||||
|
} else {
|
||||||
|
len = p - str;
|
||||||
|
if (len > sizeof(func_name) - 1)
|
||||||
|
len = sizeof(func_name) - 1;
|
||||||
|
memcpy(func_name, str, len);
|
||||||
|
func_name[len] = '\0';
|
||||||
|
}
|
||||||
|
func_addr = sym->n_value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
/* line number info */
|
||||||
|
case N_SLINE:
|
||||||
|
pc = sym->n_value + func_addr;
|
||||||
|
if (wanted_pc >= last_pc && wanted_pc < pc)
|
||||||
|
goto found;
|
||||||
|
last_pc = pc;
|
||||||
|
last_line_num = sym->n_desc;
|
||||||
|
break;
|
||||||
|
/* include files */
|
||||||
|
case N_BINCL:
|
||||||
|
str = stabstr_section->data + sym->n_strx;
|
||||||
|
add_incl:
|
||||||
|
if (incl_index < INCLUDE_STACK_SIZE) {
|
||||||
|
incl_files[incl_index++] = str;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case N_EINCL:
|
||||||
|
if (incl_index > 1)
|
||||||
|
incl_index--;
|
||||||
|
break;
|
||||||
|
case N_SO:
|
||||||
|
if (sym->n_strx == 0) {
|
||||||
|
incl_index = 0; /* end of translation unit */
|
||||||
|
} else {
|
||||||
|
str = stabstr_section->data + sym->n_strx;
|
||||||
|
/* do not add path */
|
||||||
|
len = strlen(str);
|
||||||
|
if (len > 0 && str[len - 1] != '/')
|
||||||
|
goto add_incl;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sym++;
|
||||||
|
}
|
||||||
|
/* did not find line number info: */
|
||||||
|
fprintf(stderr, "(no debug info, pc=0x%08lx) ", wanted_pc);
|
||||||
|
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 (func_name[0] != '\0') {
|
||||||
|
fprintf(stderr, "in function '%s()': ", func_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* signal handler for fatal errors */
|
||||||
|
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
|
||||||
|
|
||||||
|
rt_printline(pc);
|
||||||
|
|
||||||
|
switch(signum) {
|
||||||
|
case SIGFPE:
|
||||||
|
switch(siginf->si_code) {
|
||||||
|
case FPE_INTDIV:
|
||||||
|
case FPE_FLTDIV:
|
||||||
|
fprintf(stderr, "division by zero\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "floating point exception\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SIGBUS:
|
||||||
|
case SIGSEGV:
|
||||||
|
fprintf(stderr, "dereferencing invalid pointer\n");
|
||||||
|
break;
|
||||||
|
case SIGILL:
|
||||||
|
fprintf(stderr, "illegal instruction\n");
|
||||||
|
break;
|
||||||
|
case SIGABRT:
|
||||||
|
fprintf(stderr, "abort() called\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "signal %d\n", signum);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
exit(255);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* launch the compiled program with the given arguments */
|
||||||
|
int launch_exe(int argc, char **argv)
|
||||||
|
{
|
||||||
|
Sym *s;
|
||||||
|
int (*t)();
|
||||||
|
struct sigaction sigact;
|
||||||
|
|
||||||
|
s = sym_find1(&extern_stack, TOK_MAIN);
|
||||||
|
if (!s || (s->r & VT_FORWARD))
|
||||||
|
error("main() not defined");
|
||||||
|
|
||||||
|
if (do_debug) {
|
||||||
|
/* install TCC signal handlers to print debug info on fatal
|
||||||
|
runtime errors */
|
||||||
|
sigact.sa_flags = SA_SIGINFO | SA_ONESHOT;
|
||||||
|
sigact.sa_sigaction = sig_error;
|
||||||
|
sigemptyset(&sigact.sa_mask);
|
||||||
|
sigaction(SIGFPE, &sigact, NULL);
|
||||||
|
sigaction(SIGILL, &sigact, NULL);
|
||||||
|
sigaction(SIGSEGV, &sigact, NULL);
|
||||||
|
sigaction(SIGBUS, &sigact, NULL);
|
||||||
|
sigaction(SIGABRT, &sigact, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (do_bounds_check) {
|
||||||
|
int *p, *p_end;
|
||||||
|
__bound_init();
|
||||||
|
/* add all known static regions */
|
||||||
|
p = (int *)bounds_section->data;
|
||||||
|
p_end = (int *)bounds_section->data_ptr;
|
||||||
|
while (p < p_end) {
|
||||||
|
__bound_new_region((void *)p[0], p[1]);
|
||||||
|
p += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t = (int (*)())s->c;
|
||||||
|
return (*t)(argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void help(void)
|
void help(void)
|
||||||
{
|
{
|
||||||
printf("tcc version 0.9.3 - Tiny C Compiler - Copyright (C) 2001, 2002 Fabrice Bellard\n"
|
printf("tcc version 0.9.3 - Tiny C Compiler - Copyright (C) 2001, 2002 Fabrice Bellard\n"
|
||||||
"usage: tcc [-Idir] [-Dsym[=val]] [-Usym] [-llib] [-i infile] infile [infile_args...]\n");
|
"usage: tcc [-Idir] [-Dsym[=val]] [-Usym] [-llib] [-g] [-b]\n"
|
||||||
|
" [-i infile] infile [infile_args...]\n"
|
||||||
|
"\n"
|
||||||
|
"-Idir : add include path 'dir'\n"
|
||||||
|
"-Dsym[=val] : define 'sym' with value 'val'\n"
|
||||||
|
"-Usym : undefine 'sym'\n"
|
||||||
|
"-llib : link with dynamic library 'lib'\n"
|
||||||
|
"-g : generate debug info\n"
|
||||||
|
"-b : compile with built-in memory and bounds checker (implies -g)\n"
|
||||||
|
"-i infile : compile infile\n"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
Sym *s;
|
|
||||||
int (*t)();
|
|
||||||
char *p, *r, *outfile;
|
char *p, *r, *outfile;
|
||||||
int optind;
|
int optind;
|
||||||
|
|
||||||
|
@ -5499,9 +6011,10 @@ int main(int argc, char **argv)
|
||||||
define_symbol("__TINYC__");
|
define_symbol("__TINYC__");
|
||||||
|
|
||||||
/* create standard sections */
|
/* create standard sections */
|
||||||
text_section = new_section(".text");
|
text_section = new_section(".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR);
|
||||||
data_section = new_section(".data");
|
data_section = new_section(".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
|
||||||
bss_section = new_section(".bss");
|
/* XXX: should change type to SHT_NOBITS */
|
||||||
|
bss_section = new_section(".bss", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
|
||||||
|
|
||||||
optind = 1;
|
optind = 1;
|
||||||
outfile = NULL;
|
outfile = NULL;
|
||||||
|
@ -5529,6 +6042,37 @@ int main(int argc, char **argv)
|
||||||
if (optind >= argc)
|
if (optind >= argc)
|
||||||
goto show_help;
|
goto show_help;
|
||||||
tcc_compile_file(argv[optind++]);
|
tcc_compile_file(argv[optind++]);
|
||||||
|
} else if (r[1] == 'b') {
|
||||||
|
if (!do_bounds_check) {
|
||||||
|
do_bounds_check = 1;
|
||||||
|
/* create bounds section for global data */
|
||||||
|
bounds_section = new_section(".bounds",
|
||||||
|
SHT_PROGBITS, SHF_ALLOC);
|
||||||
|
/* debug is implied */
|
||||||
|
goto debug_opt;
|
||||||
|
}
|
||||||
|
} else if (r[1] == 'g') {
|
||||||
|
debug_opt:
|
||||||
|
if (!do_debug) {
|
||||||
|
do_debug = 1;
|
||||||
|
|
||||||
|
/* stab symbols */
|
||||||
|
stab_section = new_section(".stab", SHT_PROGBITS, 0);
|
||||||
|
stab_section->sh_entsize = sizeof(Stab_Sym);
|
||||||
|
stabstr_section = new_section(".stabstr", SHT_STRTAB, 0);
|
||||||
|
put_elf_str(stabstr_section, "");
|
||||||
|
stab_section->link = stabstr_section;
|
||||||
|
/* put first entry */
|
||||||
|
put_stabs("", 0, 0, 0, 0);
|
||||||
|
|
||||||
|
/* elf symbols */
|
||||||
|
symtab_section = new_section(".symtab", SHT_SYMTAB, 0);
|
||||||
|
symtab_section->sh_entsize = sizeof(Elf32_Sym);
|
||||||
|
strtab_section = new_section(".strtab", SHT_STRTAB, 0);
|
||||||
|
put_elf_str(strtab_section, "");
|
||||||
|
symtab_section->link = strtab_section;
|
||||||
|
put_elf_sym(symtab_section, 0, 0, 0, 0, 0, NULL);
|
||||||
|
}
|
||||||
} else if (r[1] == 'o') {
|
} else if (r[1] == 'o') {
|
||||||
/* currently, only for testing, so not documented */
|
/* currently, only for testing, so not documented */
|
||||||
if (optind >= argc)
|
if (optind >= argc)
|
||||||
|
@ -5548,10 +6092,6 @@ int main(int argc, char **argv)
|
||||||
build_exe(outfile);
|
build_exe(outfile);
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
s = sym_find1(&extern_stack, TOK_MAIN);
|
return launch_exe(argc - optind, argv + optind);
|
||||||
if (!s || (s->r & VT_FORWARD))
|
|
||||||
error("main() not defined");
|
|
||||||
t = (int (*)())s->c;
|
|
||||||
return (*t)(argc - optind, argv + optind);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue