diff options
| author | Michael Matz <matz@suse.de> | 2015-03-09 00:19:59 +0100 |
|---|---|---|
| committer | Michael Matz <matz@suse.de> | 2015-03-09 00:19:59 +0100 |
| commit | 50899e30abf3cbd71a09ecc03b2e172f6ba5d4f4 (patch) | |
| tree | 2451264d2ed2950fd232f5d4d4fed8ffe2f6538e /tccgen.c | |
| parent | d73b48840115f0adc97d6a9e6fe2aeb88fcbfbdd (diff) | |
| download | tinycc-50899e30abf3cbd71a09ecc03b2e172f6ba5d4f4.tar.gz tinycc-50899e30abf3cbd71a09ecc03b2e172f6ba5d4f4.tar.bz2 | |
Fix stack overwrite on structure return
The common code to move a returned structure packed into
registers into memory on the caller side didn't take the
register size into account when allocating local storage,
so sometimes that lead to stack overwrites (e.g. in 73_arm64.c),
on x86_64. This fixes it by generally making gfunc_sret also return
the register size.
Diffstat (limited to 'tccgen.c')
| -rw-r--r-- | tccgen.c | 20 |
1 files changed, 12 insertions, 8 deletions
@@ -4153,7 +4153,7 @@ ST_FUNC void unary(void) } else if (tok == '(') { SValue ret; Sym *sa; - int nb_args, ret_nregs, ret_align, variadic; + int nb_args, ret_nregs, ret_align, regsize, variadic; /* function call */ if ((vtop->type.t & VT_BTYPE) != VT_FUNC) { @@ -4179,7 +4179,7 @@ ST_FUNC void unary(void) if ((s->type.t & VT_BTYPE) == VT_STRUCT) { variadic = (s->c == FUNC_ELLIPSIS); ret_nregs = gfunc_sret(&s->type, variadic, &ret.type, - &ret_align); + &ret_align, ®size); if (!ret_nregs) { /* get some space for the returned structure */ size = type_size(&s->type, &align); @@ -4259,6 +4259,10 @@ ST_FUNC void unary(void) int addr, offset; size = type_size(&s->type, &align); + /* We're writing whole regs often, make sure there's enough + space. Assume register size is power of 2. */ + if (regsize > align) + align = regsize; loc = (loc - size) & -align; addr = loc; offset = 0; @@ -4269,8 +4273,7 @@ ST_FUNC void unary(void) vtop--; if (--ret_nregs == 0) break; - /* XXX: compatible with arm only: ret_align == register_size */ - offset += ret_align; + offset += regsize; } vset(&s->type, VT_LOCAL | VT_LVAL, addr); } @@ -4850,9 +4853,9 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, #else if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { CType type, ret_type; - int ret_align, ret_nregs; + int ret_align, ret_nregs, regsize; ret_nregs = gfunc_sret(&func_vt, func_var, &ret_type, - &ret_align); + &ret_align, ®size); if (0 == ret_nregs) { /* if returning structure, must copy it to implicit first pointer arg location */ @@ -4890,9 +4893,10 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, /* We assume that when a structure is returned in multiple registers, their classes are consecutive values of the suite s(n) = 2^n */ + /* XXX This seems confused, if r == 0, this never + changes r. */ r <<= 1; - /* XXX: compatible with arm only: ret_align == register_size */ - vtop->c.i += ret_align; + vtop->c.i += regsize; vtop->r = VT_LOCAL | VT_LVAL; } } |
