tccgen: flex arrays etc.

Fixes potential writes past the allocated space with mostly
illegal flex array initializers. (60_errors_and_warnings.c
:test_var_array)

In exchange suspicious precautions such as section_reserve
or checks with sec->data_allocated were removed.  (There is
an hard check 'init_assert()' for now but it's meant to be
just temporary)

Also, instead of filling holes, always memset(0) structures
& arrays on stack.  Sometimes more efficient, sometimes isn't.
At least we can omit putting null initializers.

About array range inititializers:  Reparsing tokens has a
small problem with sideeffects, for example

   int c = 0, dd[] = { [0 ... 1] = ++c, [2 ... 3] = ++c };

Also, instead of 'squeeze_multi_relocs()', delete pre-existing
relocations in advance. This works even if secondary initializers
don't even have relocations, as with
    [0 ... 7] = &stuff,
    [4] = NULL

Also, in tcc.h: new macro "tcc_internal_error()"
This commit is contained in:
grischka 2020-09-23 12:03:59 +02:00
parent 40395511d7
commit 72b520e709
9 changed files with 312 additions and 225 deletions

View File

@ -1517,7 +1517,7 @@ ST_FUNC void subst_asm_operand(CString *add_str,
} else if (r & VT_LVAL) {
reg = r & VT_VALMASK;
if (reg >= VT_CONST)
tcc_error("internal compiler error");
tcc_internal_error("");
snprintf(buf, sizeof(buf), "(%%%s)",
#ifdef TCC_TARGET_X86_64
get_tok_str(TOK_ASM_rax + reg, NULL)
@ -1530,7 +1530,7 @@ ST_FUNC void subst_asm_operand(CString *add_str,
/* register case */
reg = r & VT_VALMASK;
if (reg >= VT_CONST)
tcc_error("internal compiler error");
tcc_internal_error("");
/* choose register operand size */
if ((sv->type.t & VT_BTYPE) == VT_BYTE ||

5
tcc.h
View File

@ -1237,6 +1237,8 @@ PUB_FUNC char *tcc_strdup_debug(const char *str, const char *file, int line);
PUB_FUNC void _tcc_error_noabort(const char *fmt, ...) PRINTF_LIKE(1,2);
PUB_FUNC NORETURN void _tcc_error(const char *fmt, ...) PRINTF_LIKE(1,2);
PUB_FUNC void _tcc_warning(const char *fmt, ...) PRINTF_LIKE(1,2);
#define tcc_internal_error(msg) tcc_error("internal compiler error\n"\
"%s:%d: in %s(): " msg, __FILE__,__LINE__,__FUNCTION__)
/* other utilities */
ST_FUNC void dynarray_add(void *ptab, int *nb_ptr, void *data);
@ -1507,7 +1509,6 @@ ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh
ST_FUNC void section_realloc(Section *sec, unsigned long new_size);
ST_FUNC size_t section_add(Section *sec, addr_t size, int align);
ST_FUNC void *section_ptr_add(Section *sec, addr_t size);
ST_FUNC void section_reserve(Section *sec, unsigned long size);
ST_FUNC Section *find_section(TCCState *s1, const char *name);
ST_FUNC Section *new_symtab(TCCState *s1, const char *symtab_name, int sh_type, int sh_flags, const char *strtab_name, const char *hash_name, int hash_sh_flags);
@ -1544,8 +1545,6 @@ ST_FUNC void add_array(TCCState *s1, const char *sec, int c);
ST_FUNC void build_got_entries(TCCState *s1);
#endif
ST_FUNC struct sym_attr *get_sym_attr(TCCState *s1, int index, int alloc);
ST_FUNC void squeeze_multi_relocs(Section *sec, size_t oldrelocoffset);
ST_FUNC addr_t get_sym_addr(TCCState *s, const char *name, int err, int forc);
ST_FUNC void list_elf_symbols(TCCState *s, void *ctx,
void (*symbol_cb)(void *ctx, const char *name, const void *val));

View File

@ -321,14 +321,16 @@ ST_FUNC void *section_ptr_add(Section *sec, addr_t size)
return sec->data + offset;
}
#ifndef ELF_OBJ_ONLY
/* reserve at least 'size' bytes from section start */
ST_FUNC void section_reserve(Section *sec, unsigned long size)
static void section_reserve(Section *sec, unsigned long size)
{
if (size > sec->data_allocated)
section_realloc(sec, size);
if (size > sec->data_offset)
sec->data_offset = size;
}
#endif
static Section *find_section_create (TCCState *s1, const char *name, int create)
{
@ -762,45 +764,7 @@ ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset,
put_elf_reloca(symtab, s, offset, type, symbol, 0);
}
/* Remove relocations for section S->reloc starting at oldrelocoffset
that are to the same place, retaining the last of them. As side effect
the relocations are sorted. Possibly reduces the number of relocs. */
ST_FUNC void squeeze_multi_relocs(Section *s, size_t oldrelocoffset)
{
Section *sr = s->reloc;
ElfW_Rel *r, *dest;
ssize_t a;
ElfW(Addr) addr;
if (oldrelocoffset + sizeof(*r) >= sr->data_offset)
return;
/* The relocs we're dealing with are the result of initializer parsing.
So they will be mostly in order and there aren't many of them.
Secondly we need a stable sort (which qsort isn't). We use
a simple insertion sort. */
for (a = oldrelocoffset + sizeof(*r); a < sr->data_offset; a += sizeof(*r)) {
ssize_t i = a - sizeof(*r);
ElfW_Rel tmp = *(ElfW_Rel*)(sr->data + a);
addr = tmp.r_offset;
for (; i >= (ssize_t)oldrelocoffset &&
((ElfW_Rel*)(sr->data + i))->r_offset > addr; i -= sizeof(*r)) {
*(ElfW_Rel*)(sr->data + i + sizeof(*r)) = *(ElfW_Rel*)(sr->data + i);
}
*(ElfW_Rel*)(sr->data + i + sizeof(*r)) = tmp;
}
r = (ElfW_Rel*)(sr->data + oldrelocoffset);
dest = r;
for (; r < (ElfW_Rel*)(sr->data + sr->data_offset); r++) {
if (dest->r_offset != r->r_offset)
dest++;
*dest = *r;
}
sr->data_offset = (unsigned char*)dest - sr->data + sizeof(*r);
}
/* put stab debug information */
ST_FUNC void put_stabs(TCCState *s1, const char *str, int type, int other, int desc,
unsigned long value)
{

378
tccgen.c
View File

@ -123,6 +123,12 @@ static struct scope {
Sym *lstk, *llstk;
} *cur_scope, *loop_scope, *root_scope;
typedef struct {
Section *sec;
int local_offset;
Sym *flex_array_ref;
} init_params;
/********************************************************/
/* stab debug support */
@ -218,8 +224,8 @@ static int is_compatible_types(CType *type1, CType *type2);
static int parse_btype(CType *type, AttributeDef *ad);
static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td);
static void parse_expr_type(CType *type);
static void init_putv(CType *type, Section *sec, unsigned long c);
static void decl_initializer(CType *type, Section *sec, unsigned long c, int flags);
static void init_putv(init_params *p, CType *type, unsigned long c);
static void decl_initializer(init_params *p, CType *type, unsigned long c, int flags);
static void block(int is_expr);
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, int scope);
static void decl(int l);
@ -2167,16 +2173,17 @@ ST_FUNC int gv(int rc)
} else {
if (is_float(vtop->type.t) &&
(vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
unsigned long offset;
/* CPUs usually cannot use float constants, so we store them
generically in data segment */
init_params p = { data_section };
unsigned long offset;
size = type_size(&vtop->type, &align);
if (NODATA_WANTED)
size = 0, align = 1;
offset = section_add(data_section, size, align);
vpush_ref(&vtop->type, data_section, offset, size);
offset = section_add(p.sec, size, align);
vpush_ref(&vtop->type, p.sec, offset, size);
vswap();
init_putv(&vtop->type, data_section, offset);
init_putv(&p, &vtop->type, offset);
vtop->r |= VT_LVAL;
}
#ifdef CONFIG_TCC_BCHECK
@ -5444,7 +5451,7 @@ static void parse_builtin_params(int nc, const char *args)
type.t = VT_SIZE_T;
break;
default:
tcc_error("internal error");
break;
}
gen_assign_cast(&type);
}
@ -6102,7 +6109,8 @@ special_math_val:
problems */
vseti(VT_LOCAL, loc);
#ifdef CONFIG_TCC_BCHECK
loc -= tcc_state->do_bounds_check != 0;
if (tcc_state->do_bounds_check)
--loc;
#endif
ret.c = vtop->c;
if (ret_nregs < 0)
@ -7296,23 +7304,6 @@ static void skip_or_save_block(TokenString **str)
}
}
static void get_init_string(TokenString **str, int has_init)
{
if (has_init == 2) {
*str = tok_str_alloc();
/* only get strings */
while (tok == TOK_STR || tok == TOK_LSTR) {
tok_str_add_tok(*str);
next();
}
tok_str_add(*str, -1);
tok_str_add(*str, 0);
}
else
skip_or_save_block(str);
unget_tok(0);
}
#define EXPR_CONST 1
#define EXPR_ANY 2
@ -7343,10 +7334,22 @@ static void parse_init_elem(int expr_type)
}
}
/* put zeros for variable based init */
static void init_putz(Section *sec, unsigned long c, int size)
#if 1
static void init_assert(init_params *p, int offset)
{
if (sec) {
if (p->sec ? !NODATA_WANTED && offset > p->sec->data_offset
: !nocode_wanted && offset > p->local_offset)
tcc_internal_error("initializer overflow");
}
#else
#define init_assert(sec, offset)
#endif
/* put zeros for variable based init */
static void init_putz(init_params *p, unsigned long c, int size)
{
init_assert(p, c + size);
if (p->sec) {
/* nothing to do because globals are already set to zero */
} else {
vpush_global_sym(&func_old_type, TOK_memset);
@ -7365,6 +7368,37 @@ static void init_putz(Section *sec, unsigned long c, int size)
#define DIF_FIRST 1
#define DIF_SIZE_ONLY 2
#define DIF_HAVE_ELEM 4
#define DIF_CLEAR 8
/* delete relocations for specified range c ... c + size. Unfortunatly
in very special cases, relocations may occur unordered */
static void decl_design_delrels(Section *sec, int c, int size)
{
ElfW_Rel *rel, *rel2, *rel_end;
if (!sec || !sec->reloc)
return;
rel = rel2 = (ElfW_Rel*)sec->reloc->data;
rel_end = (ElfW_Rel*)(sec->reloc->data + sec->reloc->data_offset);
while (rel < rel_end) {
if (rel->r_offset >= c && rel->r_offset < c + size) {
sec->reloc->data_offset -= sizeof *rel;
} else {
if (rel2 != rel)
memcpy(rel2, rel, sizeof *rel);
++rel2;
}
++rel;
}
}
static void decl_design_flex(init_params *p, Sym *ref, int index)
{
if (ref == p->flex_array_ref) {
if (index >= ref->c)
ref->c = index + 1;
} else if (ref->c < 0)
tcc_error("flexible array has zero size in this context");
}
/* t is the array or struct type. c is the array or struct
address. cur_field is the pointer to the current
@ -7372,13 +7406,12 @@ static void init_putz(Section *sec, unsigned long c, int size)
index. 'flags' is as in decl_initializer.
'al' contains the already initialized length of the
current container (starting at c). This returns the new length of that. */
static int decl_designator(CType *type, Section *sec, unsigned long c,
Sym **cur_field, int flags, int al, int size)
static int decl_designator(init_params *p, CType *type, unsigned long c,
Sym **cur_field, int flags, int al)
{
Sym *s, *f;
int index, index_last, align, l, nb_elems, elem_size;
unsigned long corig = c;
TokenString *init_str = NULL;
elem_size = 0;
nb_elems = 1;
@ -7406,9 +7439,9 @@ static int decl_designator(CType *type, Section *sec, unsigned long c,
}
skip(']');
s = type->ref;
if (index < 0 || (s->c >= 0 && index_last >= s->c) ||
index_last < index)
tcc_error("invalid index");
decl_design_flex(p, s, index_last);
if (index < 0 || index_last >= s->c || index_last < index)
tcc_error("index exceeds array bounds or range is empty");
if (cur_field)
(*cur_field)->c = index_last;
type = pointed_type(type);
@ -7444,72 +7477,82 @@ static int decl_designator(CType *type, Section *sec, unsigned long c,
no_designator:
if (type->t & VT_ARRAY) {
index = (*cur_field)->c;
if (type->ref->c >= 0 && index >= type->ref->c)
tcc_error("index too large");
s = type->ref;
decl_design_flex(p, s, index);
if (index >= s->c)
tcc_error("too many initializers");
type = pointed_type(type);
c += index * type_size(type, &align);
elem_size = type_size(type, &align);
c += index * elem_size;
} else {
f = *cur_field;
while (f && (f->v & SYM_FIRST_ANOM) && (f->type.t & VT_BITFIELD))
*cur_field = f = f->next;
if (!f)
tcc_error("too many field init");
tcc_error("too many initializers");
type = &f->type;
c += f->c;
}
}
/* must put zero in holes (note that doing it that way
ensures that it even works with designators) */
if (!(flags & DIF_SIZE_ONLY)) {
int zlen = c - (corig + al);
if (type->t & VT_BITFIELD) { /* must include current field too */
zlen += type_size(type, &align);
if (al + zlen > size)
zlen = size - al;
}
if (zlen > 0)
init_putz(sec, corig + al, zlen);
if (!elem_size) /* for structs */
elem_size = type_size(type, &align);
/* Using designators the same element can be initialized more
than once. In that case we need to delete possibly already
existing relocations. */
if (!(flags & DIF_SIZE_ONLY) && c - corig < al) {
decl_design_delrels(p->sec, c, elem_size * nb_elems);
flags &= ~DIF_CLEAR; /* mark stack dirty too */
}
decl_initializer(p, type, c, flags & ~DIF_FIRST);
if (!(flags & DIF_SIZE_ONLY) && nb_elems > 1) {
get_init_string(&init_str, tok == TOK_STR || tok == TOK_LSTR ? 2 : 0);
begin_macro(init_str, 1);
next();
}
decl_initializer(type, sec, c, flags & ~DIF_FIRST);
if (!(flags & DIF_SIZE_ONLY) && nb_elems > 1) {
Sym aref = {0};
CType t1;
int i;
if (p->sec || (type->t & VT_ARRAY)) {
/* make init_putv/vstore believe it were a struct */
aref.c = elem_size;
t1.t = VT_STRUCT, t1.ref = &aref;
type = &t1;
}
if (p->sec)
vpush_ref(type, p->sec, c, elem_size);
else
vset(type, VT_LOCAL|VT_LVAL, c);
for (i = 1; i < nb_elems; i++) {
macro_ptr = init_str->str;
next();
decl_initializer(type, sec, c + i * elem_size, flags & ~DIF_FIRST);
vdup();
init_putv(p, type, c + elem_size * i);
}
end_macro();
next();
vpop();
}
c += nb_elems * type_size(type, &align);
c += nb_elems * elem_size;
if (c - corig > al)
al = c - corig;
return al;
}
/* store a value or an expression directly in global data or in local array */
static void init_putv(CType *type, Section *sec, unsigned long c)
static void init_putv(init_params *p, CType *type, unsigned long c)
{
int bt;
void *ptr;
CType dtype;
int size, align;
Section *sec = p->sec;
dtype = *type;
dtype.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */
size = type_size(type, &align);
if (type->t & VT_BITFIELD)
size = (BIT_POS(type->t) + BIT_SIZE(type->t) + 7) / 8;
init_assert(p, c + size);
if (sec) {
int size, align;
/* XXX: not portable */
/* XXX: generate error if incorrect relocation */
gen_assign_cast(&dtype);
@ -7529,8 +7572,6 @@ static void init_putv(CType *type, Section *sec, unsigned long c)
return;
}
size = type_size(type, &align);
section_reserve(sec, c + size);
ptr = sec->data + c;
/* XXX: make code faster ? */
@ -7567,12 +7608,6 @@ static void init_putv(CType *type, Section *sec, unsigned long c)
continue;
if (rel->r_offset < esym->st_value)
break;
/* Note: if the same fields are initialized multiple
times (possible with designators) then we possibly
add multiple relocations for the same offset here.
That would lead to wrong code, the last reloc needs
to win. We clean this up later after the whole
initializer is parsed. */
put_elf_reloca(symtab_section, sec,
c + rel->r_offset - esym->st_value,
ELFW(R_TYPE)(rel->r_info),
@ -7610,10 +7645,10 @@ static void init_putv(CType *type, Section *sec, unsigned long c)
case VT_BOOL:
vtop->c.i = vtop->c.i != 0;
case VT_BYTE:
*(char *)ptr |= vtop->c.i;
*(char *)ptr = vtop->c.i;
break;
case VT_SHORT:
*(short *)ptr |= vtop->c.i;
*(short *)ptr = vtop->c.i;
break;
case VT_FLOAT:
*(float*)ptr = vtop->c.f;
@ -7642,7 +7677,7 @@ static void init_putv(CType *type, Section *sec, unsigned long c)
break;
#if PTR_SIZE != 8
case VT_LLONG:
*(long long *)ptr |= vtop->c.i;
*(long long *)ptr = vtop->c.i;
break;
#else
case VT_LLONG:
@ -7654,11 +7689,11 @@ static void init_putv(CType *type, Section *sec, unsigned long c)
if (vtop->r & VT_SYM)
greloca(sec, vtop->sym, c, R_DATA_PTR, val);
else
*(addr_t *)ptr |= val;
*(addr_t *)ptr = val;
#else
if (vtop->r & VT_SYM)
greloc(sec, vtop->sym, c, R_DATA_PTR);
*(addr_t *)ptr |= val;
*(addr_t *)ptr = val;
#endif
break;
}
@ -7669,11 +7704,11 @@ static void init_putv(CType *type, Section *sec, unsigned long c)
if (vtop->r & VT_SYM)
greloca(sec, vtop->sym, c, R_DATA_PTR, val);
else
*(int *)ptr |= val;
*(int *)ptr = val;
#else
if (vtop->r & VT_SYM)
greloc(sec, vtop->sym, c, R_DATA_PTR);
*(int *)ptr |= val;
*(int *)ptr = val;
#endif
break;
}
@ -7693,8 +7728,7 @@ static void init_putv(CType *type, Section *sec, unsigned long c)
allocation. 'flags & DIF_FIRST' is true if array '{' must be read (multi
dimension implicit array init handling). 'flags & DIF_SIZE_ONLY' is true if
size only evaluation is wanted (only for arrays). */
static void decl_initializer(CType *type, Section *sec, unsigned long c,
int flags)
static void decl_initializer(init_params *p, CType *type, unsigned long c, int flags)
{
int len, n, no_oblock, i;
int size1, align1;
@ -7702,13 +7736,17 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
Sym indexsym;
CType *t1;
/* generate line number info */
if (!p->sec && tcc_state->do_debug)
tcc_debug_line(tcc_state);
if (!(flags & DIF_HAVE_ELEM) && tok != '{' &&
/* In case of strings we have special handling for arrays, so
don't consume them as initializer value (which would commit them
to some anonymous symbol). */
tok != TOK_LSTR && tok != TOK_STR &&
!(flags & DIF_SIZE_ONLY)) {
parse_init_elem(!sec ? EXPR_ANY : EXPR_CONST);
parse_init_elem(!p->sec ? EXPR_ANY : EXPR_CONST);
flags |= DIF_HAVE_ELEM;
}
@ -7718,23 +7756,21 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
The source type might have VT_CONSTANT set, which is
of course assignable to non-const elements. */
is_compatible_unqualified_types(type, &vtop->type)) {
init_putv(type, sec, c);
goto init_putv;
} else if (type->t & VT_ARRAY) {
no_oblock = 1;
if (((flags & DIF_FIRST) && tok != TOK_LSTR && tok != TOK_STR) ||
tok == '{') {
skip('{');
no_oblock = 0;
}
s = type->ref;
n = s->c;
t1 = pointed_type(type);
size1 = type_size(t1, &align1);
no_oblock = 1;
if (((flags & DIF_FIRST) && tok != TOK_LSTR && tok != TOK_STR) ||
tok == '{') {
if (tok != '{')
tcc_error("character array initializer must be a literal,"
" optionally enclosed in braces");
skip('{');
no_oblock = 0;
}
/* only parse strings here if correct type (otherwise: handle
them as ((w)char *) expressions */
if ((tok == TOK_LSTR &&
@ -7744,7 +7780,6 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
(t1->t & VT_BTYPE) == VT_INT
#endif
) || (tok == TOK_STR && (t1->t & VT_BTYPE) == VT_BYTE)) {
int nb;
len = 0;
cstr_reset(&initstr);
if (size1 != (tok == TOK_STR ? 1 : sizeof(nwchar_t)))
@ -7766,54 +7801,61 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
unget_tok(size1 == 1 ? TOK_STR : TOK_LSTR);
tokc.str.size = initstr.size;
tokc.str.data = initstr.data;
indexsym.c = 0;
f = &indexsym;
goto do_init_list;
goto do_init_array;
}
nb = len;
if (n >= 0 && len > n)
nb = n;
if (!(flags & DIF_SIZE_ONLY)) {
if (sec && !NODATA_WANTED &&
(c + nb > sec->data_allocated))
nb = sec->data_allocated - c;
int nb = n;
if (len < nb)
nb = len;
if (len > nb)
tcc_warning("initializer-string for array is too long");
/* in order to go faster for common case (char
string in global variable, we handle it
specifically */
if (sec && size1 == 1) {
if (p->sec && size1 == 1) {
init_assert(p, c + nb);
if (!NODATA_WANTED)
memcpy(sec->data + c, initstr.data, nb);
memcpy(p->sec->data + c, initstr.data, nb);
} else {
for(i=0;i<nb;i++) {
if (size1 == 1)
for(i=0;i<n;i++) {
if (i >= nb) {
/* only add trailing zero if enough storage (no
warning in this case since it is standard) */
if (flags & DIF_CLEAR)
break;
if (n - i >= 4) {
init_putz(p, c + i * size1, (n - i) * size1);
break;
}
ch = 0;
} else if (size1 == 1)
ch = ((unsigned char *)initstr.data)[i];
else
ch = ((nwchar_t *)initstr.data)[i];
vpushi(ch);
init_putv(t1, sec, c + i * size1);
init_putv(p, t1, c + i * size1);
}
}
}
/* only add trailing zero if enough storage (no
warning in this case since it is standard) */
if (n < 0 || len < n) {
if (!(flags & DIF_SIZE_ONLY)) {
vpushi(0);
init_putv(t1, sec, c + (len * size1));
}
len++;
}
len *= size1;
} else {
decl_design_flex(p, s, len);
}
} else {
do_init_array:
indexsym.c = 0;
f = &indexsym;
do_init_list:
/* zero memory once in advance */
if (!(flags & (DIF_CLEAR | DIF_SIZE_ONLY))) {
init_putz(p, c, n*size1);
flags |= DIF_CLEAR;
}
len = 0;
while (tok != '}' || (flags & DIF_HAVE_ELEM)) {
len = decl_designator(type, sec, c, &f, flags, len, n*size1);
len = decl_designator(p, type, c, &f, flags, len);
flags &= ~DIF_HAVE_ELEM;
if (type->t & VT_ARRAY) {
++indexsym.c;
@ -7836,16 +7878,9 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
skip(',');
}
}
/* put zeros at the end */
if (!(flags & DIF_SIZE_ONLY) && len < n*size1)
init_putz(sec, c + len, n*size1 - len);
if (!no_oblock)
skip('}');
/* patch type size if needed, which happens only for array types */
if (n < 0)
s->c = size1 == 1 ? len : ((len + size1 - 1)/size1);
} else if ((type->t & VT_BTYPE) == VT_STRUCT) {
size1 = 1;
no_oblock = 1;
if ((flags & DIF_FIRST) || tok == '{') {
skip('{');
@ -7854,12 +7889,13 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
s = type->ref;
f = s->next;
n = s->c;
size1 = 1;
goto do_init_list;
} else if (tok == '{') {
if (flags & DIF_HAVE_ELEM)
skip(';');
next();
decl_initializer(type, sec, c, flags & ~DIF_HAVE_ELEM);
decl_initializer(p, type, c, flags & ~DIF_HAVE_ELEM);
skip('}');
} else if ((flags & DIF_SIZE_ONLY)) {
/* If we supported only ISO C we wouldn't have to accept calling
@ -7877,9 +7913,17 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
string constant to memory too early. */
if (tok != TOK_STR && tok != TOK_LSTR)
expect("string constant");
parse_init_elem(!sec ? EXPR_ANY : EXPR_CONST);
parse_init_elem(!p->sec ? EXPR_ANY : EXPR_CONST);
}
init_putv(type, sec, c);
init_putv:
if (!p->sec && (flags & DIF_CLEAR) /* container was already zero'd */
&& (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST
&& vtop->c.i == 0
&& btype_size(type->t & VT_BTYPE) /* not for fp constants */
)
vpop();
else
init_putv(p, type, c);
}
}
@ -7903,38 +7947,60 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
#ifdef CONFIG_TCC_BCHECK
int bcheck = tcc_state->do_bounds_check && !NODATA_WANTED;
#endif
init_params p = {0};
/* Always allocate static or global variables */
if (v && (r & VT_VALMASK) == VT_CONST)
nocode_wanted |= 0x80000000;
flexible_array = NULL;
if ((type->t & VT_BTYPE) == VT_STRUCT) {
size = type_size(type, &align);
/* exactly one flexible array may be initialized, either the
toplevel array or the last member of the toplevel struct */
if (size < 0) {
/* If the base type itself was an array type of unspecified size
(like in 'typedef int arr[]; arr x = {1};') then we will
overwrite the unknown size by the real one for this decl.
We need to unshare the ref symbol holding that size. */
type->ref = sym_push(SYM_FIELD, &type->ref->type, 0, type->ref->c);
p.flex_array_ref = type->ref;
} else if (has_init && (type->t & VT_BTYPE) == VT_STRUCT) {
Sym *field = type->ref->next;
if (field) {
while (field->next)
field = field->next;
if (field->type.t & VT_ARRAY && field->type.ref->c < 0)
if (field->type.t & VT_ARRAY && field->type.ref->c < 0) {
flexible_array = field;
p.flex_array_ref = field->type.ref;
size = -1;
}
}
}
size = type_size(type, &align);
/* If unknown size, we must evaluate it before
evaluating initializers because
initializers can generate global data too
(e.g. string pointers or ISOC99 compound
literals). It also simplifies local
initializers handling */
if (size < 0 || (flexible_array && has_init)) {
if (size < 0) {
/* If unknown size, do a dry-run 1st pass */
if (!has_init)
tcc_error("unknown type size");
get_init_string(&init_str, has_init);
if (has_init == 2) {
/* only get strings */
init_str = tok_str_alloc();
while (tok == TOK_STR || tok == TOK_LSTR) {
tok_str_add_tok(init_str);
next();
}
tok_str_add(init_str, -1);
tok_str_add(init_str, 0);
} else
skip_or_save_block(&init_str);
unget_tok(0);
/* compute size */
begin_macro(init_str, 1);
next();
decl_initializer(type, NULL, 0, DIF_FIRST | DIF_SIZE_ONLY);
decl_initializer(&p, type, 0, DIF_FIRST | DIF_SIZE_ONLY);
/* prepare second initializer parsing */
macro_ptr = init_str->str;
next();
@ -7943,13 +8009,14 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
size = type_size(type, &align);
if (size < 0)
tcc_error("unknown type size");
}
/* If there's a flex member and it was used in the initializer
adjust size. */
if (flexible_array &&
flexible_array->type.ref->c > 0)
if (flexible_array && flexible_array->type.ref->c > 0)
size += flexible_array->type.ref->c
* pointed_size(&flexible_array->type);
}
/* take into account specified alignment if bigger */
if (ad->a.aligned) {
int speca = 1 << (ad->a.aligned - 1);
@ -7972,6 +8039,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
#endif
loc = (loc - size) & -align;
addr = loc;
p.local_offset = addr + size;
#ifdef CONFIG_TCC_BCHECK
if (bcheck && v) {
/* add padding between stack variables for bound checking */
@ -8088,12 +8156,8 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
cur_scope->vla.loc = addr;
cur_scope->vla.num++;
} else if (has_init) {
size_t oldreloc_offset = 0;
if (sec && sec->reloc)
oldreloc_offset = sec->reloc->data_offset;
decl_initializer(type, sec, addr, DIF_FIRST);
if (sec && sec->reloc)
squeeze_multi_relocs(sec, oldreloc_offset);
p.sec = sec;
decl_initializer(&p, type, addr, DIF_FIRST);
/* patch flexible array member size back to -1, */
/* for possible subsequent similar declarations */
if (flexible_array)
@ -8288,14 +8352,6 @@ static int decl0(int l, int is_for_loop_init, Sym *func_sym)
}
while (1) { /* iterate thru each declaration */
type = btype;
/* If the base type itself was an array type of unspecified
size (like in 'typedef int arr[]; arr x = {1};') then
we will overwrite the unknown size by the real one for
this decl. We need to unshare the ref symbol holding
that size. */
if ((type.t & VT_ARRAY) && type.ref->c < 0) {
type.ref = sym_push(SYM_FIELD, &type.ref->type, 0, type.ref->c);
}
ad = adbase;
type_decl(&type, &ad, &v, TYPE_DIRECT);
#if 0

15
tccpp.c
View File

@ -2586,7 +2586,7 @@ static void parse_number(const char *p)
tokc.i = n;
}
if (ch)
tcc_error("invalid number\n");
tcc_error("invalid number");
}
@ -3674,20 +3674,20 @@ static void tcc_predefs(CString *cstr)
"#define __builtin_va_copy(dest,src) (dest)=(src)\n"
"#endif\n"
"#ifdef __leading_underscore\n"
"#define RENAME(X) __asm__(\"_\"X)\n"
"#define __RENAME(X) __asm__(\"_\"X)\n"
"#else\n"
"#define RENAME(X) __asm__(X)\n"
"#define __RENAME(X) __asm__(X)\n"
"#endif\n"
/* TCC BBUILTIN AND BOUNDS ALIASES */
"#ifdef __BOUNDS_CHECKING_ON\n"
"#define __BUILTINBC(ret,name,params) ret __builtin_##name params RENAME(\"__bound_\"#name);\n"
"#define __BOUND(ret,name,params) ret name params RENAME(\"__bound_\"#name);\n"
"#define __BUILTINBC(ret,name,params) ret __builtin_##name params __RENAME(\"__bound_\"#name);\n"
"#define __BOUND(ret,name,params) ret name params __RENAME(\"__bound_\"#name);\n"
"#else\n"
"#define __BUILTINBC(ret,name,params) ret __builtin_##name params RENAME(#name);\n"
"#define __BUILTINBC(ret,name,params) ret __builtin_##name params __RENAME(#name);\n"
"#define __BOUND(ret,name,params)\n"
"#endif\n"
"#define __BOTH(ret,name,params) __BUILTINBC(ret,name,params)__BOUND(ret,name,params)\n"
"#define __BUILTIN(ret,name,params) ret __builtin_##name params RENAME(#name);\n"
"#define __BUILTIN(ret,name,params) ret __builtin_##name params __RENAME(#name);\n"
"__BOTH(void*,memcpy,(void*,const void*,__SIZE_TYPE__))\n"
"__BOTH(void*,memmove,(void*,const void*,__SIZE_TYPE__))\n"
"__BOTH(void*,memset,(void*,int,__SIZE_TYPE__))\n"
@ -3731,6 +3731,7 @@ static void tcc_predefs(CString *cstr)
"#undef __BOUND\n"
"#undef __BOTH\n"
"#undef __MAYBE_REDIR\n"
"#undef __RENAME\n"
, -1);
}

View File

@ -346,4 +346,13 @@ static struct var_len { int i; const char str[]; } var_array[] =
{ 2, "longlonglonglonglong" },
{ 3, "tst3" } };
#elif defined test_var_array2
struct c1 { int a; int b[]; };
struct c1 c1 = { 1, { 2, 3, 4 } };
struct c2 { int c; struct c1 c1; };
struct c2 c2 = { 1, { 2, { 3, 4, 5 }}};
/******************************************************************/
#endif

View File

@ -161,4 +161,7 @@ bar : 3 ; 3
\n
[test_var_array]
60_errors_and_warnings.c:345: warning: initializer-string for array is too long
60_errors_and_warnings.c:345: error: flexible array has zero size in this context
[test_var_array2]
60_errors_and_warnings.c:355: error: flexible array has zero size in this context

View File

@ -221,19 +221,66 @@ void sys_ni(void) { printf("ni\n"); }
void sys_one(void) { printf("one\n"); }
void sys_two(void) { printf("two\n"); }
void sys_three(void) { printf("three\n"); }
void sys_four(void) { printf("four\n"); }
typedef void (*fptr)(void);
const fptr table[3] = {
[0 ... 2] = &sys_ni,
[0] = sys_one,
[1] = sys_two,
[2] = sys_three,
};
#define array_size(a) (sizeof a / sizeof a[0])
void test_multi_relocs(void)
{
int i;
for (i = 0; i < sizeof(table)/sizeof(table[0]); i++)
table[i]();
static const fptr tabl1[4] = {
[0 ... 3] = &sys_ni,
[0] = sys_one,
[1] = sys_two,
[2] = sys_three,
sys_four,
[1 ... 2] = &sys_ni,
[1] = 0,
};
for (i = 0; i < array_size(tabl1); i++)
if (tabl1[i])
tabl1[i]();
else
printf("(0)\n");
const fptr tabl2[4] = {
[0 ... 3] = &sys_ni,
[0] = sys_one,
[1] = sys_two,
[2] = sys_three,
sys_four,
[1 ... 2] = &sys_ni,
[1] = 0,
};
for (i = 0; i < array_size(tabl2); i++)
if (tabl2[i])
tabl2[i]();
else
printf("(0)\n");
int c = 0;
int dd[] = {
[0 ... 1] = ++c,
[2 ... 3] = ++c
};
for (i = 0; i < array_size(dd); i++)
printf(" %d", dd[i]);
printf("\n");
/* multi-dimensional flex array with range initializers */
static char m1[][2][3] = {[0 ... 2]={{3,4,5},{6,7,8}},{{9},10},"abc"};
char m2[][2][3] = {[0 ... 2]={{3,4,5},{6,7,8}},{{9},10},"abc"};
int g, j, k;
for (g = 2; g-- > 0;) {
printf("mdfa %s: %d -", "locl\0glob" + g * 5, sizeof m1);
for (i = 0; i < array_size(m1); i++)
for (j = 0; j < array_size(m1[0]); j++)
for (k = 0; k < array_size(m1[0][0]); k++)
printf(" %d", (g ? m1:m2)[i][j][k]);
printf("\n");
}
}
void test_init_ranges(void) {

View File

@ -41,8 +41,16 @@ lssu2: 5 0 0 0 3 0 0 0
flow: 9 8 7 6 0 0 0 0 0 0 0 0 0 0 0 0 6 5 4 3 0 0 0 0 0 0 0 0 0 0 0 0
ls4: 1 2 3 4
one
two
three
(0)
ni
four
one
(0)
ni
four
1 1 2 2
mdfa glob: 30 - 3 4 5 6 7 8 3 4 5 6 7 8 3 4 5 6 7 8 9 0 0 10 0 0 97 98 99 0 0 0
mdfa locl: 30 - 3 4 5 6 7 8 3 4 5 6 7 8 3 4 5 6 7 8 9 0 0 10 0 0 97 98 99 0 0 0
sea_fill0: okay
sea_fill1: okay
sea_fill2: okay