From 72b520e709d608f0450226bdfdc7b5bdb223aebf Mon Sep 17 00:00:00 2001 From: grischka Date: Wed, 23 Sep 2020 12:03:59 +0200 Subject: [PATCH] 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()" --- i386-asm.c | 4 +- tcc.h | 5 +- tccelf.c | 42 +-- tccgen.c | 382 ++++++++++++--------- tccpp.c | 15 +- tests/tests2/60_errors_and_warnings.c | 9 + tests/tests2/60_errors_and_warnings.expect | 5 +- tests/tests2/90_struct-init.c | 63 +++- tests/tests2/90_struct-init.expect | 12 +- 9 files changed, 312 insertions(+), 225 deletions(-) diff --git a/i386-asm.c b/i386-asm.c index 3f7c1df9..10926fef 100644 --- a/i386-asm.c +++ b/i386-asm.c @@ -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 || diff --git a/tcc.h b/tcc.h index 410d3e67..5e987ed4 100644 --- a/tcc.h +++ b/tcc.h @@ -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)); diff --git a/tccelf.c b/tccelf.c index f9449b30..aa582856 100644 --- a/tccelf.c +++ b/tccelf.c @@ -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) { diff --git a/tccgen.c b/tccgen.c index 84b9420c..681fa192 100644 --- a/tccgen.c +++ b/tccgen.c @@ -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 */ } - 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); + decl_initializer(p, type, c, flags & ~DIF_FIRST); if (!(flags & DIF_SIZE_ONLY) && nb_elems > 1) { + Sym aref = {0}; + CType t1; int i; - - for(i = 1; i < nb_elems; i++) { - macro_ptr = init_str->str; - next(); - decl_initializer(type, sec, c + i * elem_size, flags & ~DIF_FIRST); + 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; } - end_macro(); - next(); + 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++) { + vdup(); + init_putv(p, type, c + elem_size * i); + } + 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) { + /* 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); } } + } else { + decl_design_flex(p, s, len); } - /* 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 { + + 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) + size += flexible_array->type.ref->c + * pointed_size(&flexible_array->type); } - /* If there's a flex member and it was used in the initializer - adjust size. */ - 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 diff --git a/tccpp.c b/tccpp.c index 74ce9954..2c0dab94 100644 --- a/tccpp.c +++ b/tccpp.c @@ -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); } diff --git a/tests/tests2/60_errors_and_warnings.c b/tests/tests2/60_errors_and_warnings.c index fd4367a1..ede00186 100644 --- a/tests/tests2/60_errors_and_warnings.c +++ b/tests/tests2/60_errors_and_warnings.c @@ -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 diff --git a/tests/tests2/60_errors_and_warnings.expect b/tests/tests2/60_errors_and_warnings.expect index 984e4c18..8705f40b 100644 --- a/tests/tests2/60_errors_and_warnings.expect +++ b/tests/tests2/60_errors_and_warnings.expect @@ -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 diff --git a/tests/tests2/90_struct-init.c b/tests/tests2/90_struct-init.c index e3535bd0..1dae3c92 100644 --- a/tests/tests2/90_struct-init.c +++ b/tests/tests2/90_struct-init.c @@ -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) { diff --git a/tests/tests2/90_struct-init.expect b/tests/tests2/90_struct-init.expect index 70b9de1b..1b7b171e 100644 --- a/tests/tests2/90_struct-init.expect +++ b/tests/tests2/90_struct-init.expect @@ -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