mirror of
https://github.com/frida/tinycc
synced 2024-12-25 06:26:49 +03:00
primitive fastcall functions support - -fno-common option - -Ttext linker option - bit field fixes - section alignment fixes
This commit is contained in:
parent
72a88fbab0
commit
9668499b9f
130
tcc.c
130
tcc.c
@ -205,12 +205,12 @@ typedef struct DLLReference {
|
||||
typedef struct AttributeDef {
|
||||
int aligned;
|
||||
Section *section;
|
||||
unsigned char func_call; /* FUNC_CDECL or FUNC_STDCALL */
|
||||
unsigned char func_call; /* FUNC_CDECL, FUNC_STDCALL, FUNC_FASTCALLx */
|
||||
} AttributeDef;
|
||||
|
||||
#define SYM_STRUCT 0x40000000 /* struct/union/enum symbol space */
|
||||
#define SYM_FIELD 0x20000000 /* struct/union field symbol space */
|
||||
#define SYM_FIRST_ANOM (1 << (31 - VT_STRUCT_SHIFT)) /* first anonymous sym */
|
||||
#define SYM_FIRST_ANOM 0x10000000 /* first anonymous sym */
|
||||
|
||||
/* stored in 'Sym.c' field */
|
||||
#define FUNC_NEW 1 /* ansi function prototype */
|
||||
@ -220,6 +220,9 @@ typedef struct AttributeDef {
|
||||
/* stored in 'Sym.r' field */
|
||||
#define FUNC_CDECL 0 /* standard c call */
|
||||
#define FUNC_STDCALL 1 /* pascal c call */
|
||||
#define FUNC_FASTCALL1 2 /* first param in %eax */
|
||||
#define FUNC_FASTCALL2 3 /* first parameters in %eax, %edx */
|
||||
#define FUNC_FASTCALL3 4 /* first parameter in %eax, %edx, %ecx */
|
||||
|
||||
/* field 'Sym.t' for macros */
|
||||
#define MACRO_OBJ 0 /* object like macro */
|
||||
@ -299,10 +302,12 @@ static int parse_flags;
|
||||
#define PARSE_FLAG_LINEFEED 0x0004 /* line feed is returned as a
|
||||
token. line feed is also
|
||||
returned at eof */
|
||||
#define PARSE_FLAG_ASM_COMMENTS 0x0008 /* '#' can be used for line comment */
|
||||
|
||||
static Section *text_section, *data_section, *bss_section; /* predefined sections */
|
||||
static Section *cur_text_section; /* current section where function code is
|
||||
generated */
|
||||
static Section *last_text_section; /* to handle .previous asm directive */
|
||||
/* bound check related sections */
|
||||
static Section *bounds_section; /* contains global data bound description */
|
||||
static Section *lbounds_section; /* contains local data bound description */
|
||||
@ -413,6 +418,8 @@ struct TCCState {
|
||||
int nostdinc; /* if true, no standard headers are added */
|
||||
int nostdlib; /* if true, no standard libraries are added */
|
||||
|
||||
int nocommon; /* if true, do not use common symbols for .bss data */
|
||||
|
||||
/* if true, static linking is performed */
|
||||
int static_link;
|
||||
|
||||
@ -422,6 +429,10 @@ struct TCCState {
|
||||
/* if true, only link in referenced objects from archive */
|
||||
int alacarte_link;
|
||||
|
||||
/* address of text section */
|
||||
unsigned long text_addr;
|
||||
int has_text_addr;
|
||||
|
||||
/* C language options */
|
||||
int char_is_unsigned;
|
||||
|
||||
@ -789,9 +800,12 @@ typedef struct ASMOperand {
|
||||
char asm_str[16]; /* computed asm string for operand */
|
||||
SValue *vt; /* C value of the expression */
|
||||
int ref_index; /* if >= 0, gives reference to a output constraint */
|
||||
int input_index; /* if >= 0, gives reference to an input constraint */
|
||||
int priority; /* priority, used to assign registers */
|
||||
int reg; /* if >= 0, register number used for this operand */
|
||||
int is_llong; /* true if double register value */
|
||||
int is_memory; /* true if memory operand */
|
||||
int is_rw; /* for '+' modifier */
|
||||
} ASMOperand;
|
||||
|
||||
static void asm_expr(TCCState *s1, ExprValue *pe);
|
||||
@ -804,6 +818,7 @@ static int tcc_assemble(TCCState *s1, int do_preprocess);
|
||||
#endif
|
||||
|
||||
static void asm_instr(void);
|
||||
static void asm_global_instr(void);
|
||||
|
||||
/* true if float/double/long double type */
|
||||
static inline int is_float(int t)
|
||||
@ -914,6 +929,22 @@ static char *pstrcat(char *buf, int buf_size, const char *s)
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int strstart(const char *str, const char *val, const char **ptr)
|
||||
{
|
||||
const char *p, *q;
|
||||
p = str;
|
||||
q = val;
|
||||
while (*q != '\0') {
|
||||
if (*p != *q)
|
||||
return 0;
|
||||
p++;
|
||||
q++;
|
||||
}
|
||||
if (ptr)
|
||||
*ptr = p;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* memory management */
|
||||
#ifdef MEM_DEBUG
|
||||
int mem_cur_size;
|
||||
@ -1086,6 +1117,8 @@ Section *find_section(TCCState *s1, const char *name)
|
||||
return new_section(s1, name, SHT_PROGBITS, SHF_ALLOC);
|
||||
}
|
||||
|
||||
#define SECTION_ABS ((void *)1)
|
||||
|
||||
/* update sym->c so that it points to an external symbol in section
|
||||
'section' with value 'value' */
|
||||
static void put_extern_sym(Sym *sym, Section *section,
|
||||
@ -1095,10 +1128,12 @@ static void put_extern_sym(Sym *sym, Section *section,
|
||||
Elf32_Sym *esym;
|
||||
const char *name;
|
||||
|
||||
if (section)
|
||||
sh_num = section->sh_num;
|
||||
else
|
||||
if (section == NULL)
|
||||
sh_num = SHN_UNDEF;
|
||||
else if (section == SECTION_ABS)
|
||||
sh_num = SHN_ABS;
|
||||
else
|
||||
sh_num = section->sh_num;
|
||||
if (!sym->c) {
|
||||
if ((sym->type.t & VT_BTYPE) == VT_FUNC)
|
||||
sym_type = STT_FUNC;
|
||||
@ -2850,7 +2885,8 @@ static void preprocess(int is_bof)
|
||||
/* '!' is ignored to allow C scripts. numbers are ignored
|
||||
to emulate cpp behaviour */
|
||||
} else {
|
||||
error("invalid preprocessing directive #%s", get_tok_str(tok, &tokc));
|
||||
if (!(saved_parse_flags & PARSE_FLAG_ASM_COMMENTS))
|
||||
error("invalid preprocessing directive #%s", get_tok_str(tok, &tokc));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -3367,7 +3403,12 @@ static inline void next_nomacro1(void)
|
||||
p++;
|
||||
tok = TOK_TWOSHARPS;
|
||||
} else {
|
||||
tok = '#';
|
||||
if (parse_flags & PARSE_FLAG_ASM_COMMENTS) {
|
||||
p = parse_line_comment(p - 1);
|
||||
goto redo_no_start;
|
||||
} else {
|
||||
tok = '#';
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -3815,7 +3856,6 @@ static int macro_subst_tok(TokenString *tok_str,
|
||||
char buf[32];
|
||||
|
||||
/* if symbol is a macro, prepare substitution */
|
||||
|
||||
/* special macros */
|
||||
if (tok == TOK___LINE__) {
|
||||
snprintf(buf, sizeof(buf), "%d", file->line_num);
|
||||
@ -5745,6 +5785,9 @@ static int is_compatible_func(CType *type1, CType *type2)
|
||||
s2 = type2->ref;
|
||||
if (!is_compatible_types(&s1->type, &s2->type))
|
||||
return 0;
|
||||
/* check func_call */
|
||||
if (s1->r != s2->r)
|
||||
return 0;
|
||||
/* XXX: not complete */
|
||||
if (s1->c == FUNC_OLD || s2->c == FUNC_OLD)
|
||||
return 1;
|
||||
@ -5985,7 +6028,8 @@ void vstore(void)
|
||||
warning("assignment of read-only location");
|
||||
} else {
|
||||
delayed_cast = 0;
|
||||
gen_assign_cast(&vtop[-1].type);
|
||||
if (!(ft & VT_BITFIELD))
|
||||
gen_assign_cast(&vtop[-1].type);
|
||||
}
|
||||
|
||||
if (sbt == VT_STRUCT) {
|
||||
@ -6164,6 +6208,20 @@ static void parse_attribute(AttributeDef *ad)
|
||||
case TOK_STDCALL3:
|
||||
ad->func_call = FUNC_STDCALL;
|
||||
break;
|
||||
#ifdef TCC_TARGET_I386
|
||||
case TOK_REGPARM1:
|
||||
case TOK_REGPARM2:
|
||||
skip('(');
|
||||
n = expr_const();
|
||||
if (n > 3)
|
||||
n = 3;
|
||||
else if (n < 0)
|
||||
n = 0;
|
||||
if (n > 0)
|
||||
ad->func_call = FUNC_FASTCALL1 + n - 1;
|
||||
skip(')');
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
if (tcc_state->warn_unsupported)
|
||||
warning("'%s' attribute ignored", get_tok_str(t, NULL));
|
||||
@ -6280,6 +6338,8 @@ static void struct_decl(CType *type, int u)
|
||||
get_tok_str(v, NULL));
|
||||
}
|
||||
size = type_size(&type1, &align);
|
||||
if (align < ad.aligned)
|
||||
align = ad.aligned;
|
||||
lbit_pos = 0;
|
||||
if (bit_size >= 0) {
|
||||
bt = type1.t & VT_BTYPE;
|
||||
@ -8418,6 +8478,12 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
|
||||
if (sym->type.t & VT_EXTERN) {
|
||||
/* if the variable is extern, it was not allocated */
|
||||
sym->type.t &= ~VT_EXTERN;
|
||||
/* set array size if it was ommited in extern
|
||||
declaration */
|
||||
if ((sym->type.t & VT_ARRAY) &&
|
||||
sym->type.ref->c < 0 &&
|
||||
type->ref->c >= 0)
|
||||
sym->type.ref->c = type->ref->c;
|
||||
} else {
|
||||
/* we accept several definitions of the same
|
||||
global variable. this is tricky, because we
|
||||
@ -8437,6 +8503,8 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
|
||||
if (!sec) {
|
||||
if (has_init)
|
||||
sec = data_section;
|
||||
else if (tcc_state->nocommon)
|
||||
sec = bss_section;
|
||||
}
|
||||
if (sec) {
|
||||
data_offset = sec->data_offset;
|
||||
@ -8453,6 +8521,9 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
|
||||
if (sec->sh_type != SHT_NOBITS &&
|
||||
data_offset > sec->data_allocated)
|
||||
section_realloc(sec, data_offset);
|
||||
/* align section if needed */
|
||||
if (align > sec->sh_addralign)
|
||||
sec->sh_addralign = align;
|
||||
} else {
|
||||
addr = 0; /* avoid warning */
|
||||
}
|
||||
@ -8646,6 +8717,12 @@ static void decl(int l)
|
||||
next();
|
||||
continue;
|
||||
}
|
||||
if (l == VT_CONST &&
|
||||
(tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) {
|
||||
/* global asm block */
|
||||
asm_global_instr();
|
||||
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)
|
||||
@ -8683,7 +8760,6 @@ static void decl(int l)
|
||||
ParseState saved_parse_state;
|
||||
int block_level;
|
||||
#endif
|
||||
|
||||
if (l == VT_LOCAL)
|
||||
error("cannot use local functions");
|
||||
if (!(type.t & VT_FUNC))
|
||||
@ -8738,6 +8814,19 @@ static void decl(int l)
|
||||
funcname = get_tok_str(v, NULL);
|
||||
sym = sym_find(v);
|
||||
if (sym) {
|
||||
if ((sym->type.t & VT_BTYPE) != VT_FUNC)
|
||||
goto func_error1;
|
||||
/* specific case: if not func_call defined, we put
|
||||
the one of the prototype */
|
||||
/* XXX: should have default value */
|
||||
if (sym->type.ref->r != FUNC_CDECL &&
|
||||
type.ref->r == FUNC_CDECL)
|
||||
type.ref->r = sym->type.ref->r;
|
||||
if (!is_compatible_types(&sym->type, &type)) {
|
||||
func_error1:
|
||||
error("incompatible types for redefinition of '%s'",
|
||||
get_tok_str(v, NULL));
|
||||
}
|
||||
/* if symbol is already defined, then put complete type */
|
||||
sym->type = type;
|
||||
} else {
|
||||
@ -8790,6 +8879,9 @@ static void decl(int l)
|
||||
sym->type.t |= VT_TYPEDEF;
|
||||
} else if ((type.t & VT_BTYPE) == VT_FUNC) {
|
||||
/* external function definition */
|
||||
/* specific case for func_call attribute */
|
||||
if (ad.func_call)
|
||||
type.ref->r = ad.func_call;
|
||||
external_sym(v, &type, 0);
|
||||
} else {
|
||||
/* not lvalue if array */
|
||||
@ -9013,6 +9105,10 @@ static void asm_instr(void)
|
||||
{
|
||||
error("inline asm() not supported");
|
||||
}
|
||||
static void asm_global_instr(void)
|
||||
{
|
||||
error("inline asm() not supported");
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "tccelf.c"
|
||||
@ -9794,6 +9890,7 @@ int tcc_set_warning(TCCState *s, const char *warning_name, int value)
|
||||
static const FlagDef flag_defs[] = {
|
||||
{ offsetof(TCCState, char_is_unsigned), 0, "unsigned-char" },
|
||||
{ offsetof(TCCState, char_is_unsigned), FD_INVERT, "signed-char" },
|
||||
{ offsetof(TCCState, nocommon), FD_INVERT, "common" },
|
||||
};
|
||||
|
||||
/* set/reset a flag */
|
||||
@ -9897,6 +9994,7 @@ enum {
|
||||
TCC_OPTION_shared,
|
||||
TCC_OPTION_o,
|
||||
TCC_OPTION_r,
|
||||
TCC_OPTION_Wl,
|
||||
TCC_OPTION_W,
|
||||
TCC_OPTION_O,
|
||||
TCC_OPTION_m,
|
||||
@ -9932,6 +10030,7 @@ static const TCCOption tcc_options[] = {
|
||||
{ "run", TCC_OPTION_run, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
|
||||
{ "rdynamic", TCC_OPTION_rdynamic, 0 },
|
||||
{ "r", TCC_OPTION_r, 0 },
|
||||
{ "Wl,", TCC_OPTION_Wl, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
|
||||
{ "W", TCC_OPTION_W, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
|
||||
{ "O", TCC_OPTION_O, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
|
||||
{ "m", TCC_OPTION_m, TCC_OPTION_HAS_ARG },
|
||||
@ -10144,6 +10243,17 @@ int parse_args(TCCState *s, int argc, char **argv)
|
||||
case TCC_OPTION_rdynamic:
|
||||
s->rdynamic = 1;
|
||||
break;
|
||||
case TCC_OPTION_Wl:
|
||||
{
|
||||
const char *p;
|
||||
if (strstart(optarg, "-Ttext,", &p)) {
|
||||
s->text_addr = strtoul(p, NULL, 16);
|
||||
s->has_text_addr = 1;
|
||||
} else {
|
||||
error("unsupported ld option '%s'", optarg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (s->warn_unsupported) {
|
||||
unsupported_option:
|
||||
|
Loading…
Reference in New Issue
Block a user