Stripped down TinyCC fork for StupidOS
On i386 and gcc-4.7 I found that __bound_local_new was miscompiled -
look:
#ifdef __i386__
/* return the frame pointer of the caller */
#define GET_CALLER_FP(fp)\
{\
unsigned long *fp1;\
__asm__ __volatile__ ("movl %%ebp,%0" :"=g" (fp1));\
fp = fp1[0];\
}
#endif
/* called when entering a function to add all the local regions */
void FASTCALL __bound_local_new(void *p1)
{
unsigned long addr, size, fp, *p = p1;
GET_CALLER_FP(fp);
for(;;) {
addr = p[0];
if (addr == 0)
break;
addr += fp;
size = p[1];
p += 2;
__bound_new_region((void *)addr, size);
}
}
__bound_local_new:
.LFB40:
.cfi_startproc
pushl %esi
.cfi_def_cfa_offset 8
.cfi_offset 6, -8
pushl %ebx
.cfi_def_cfa_offset 12
.cfi_offset 3, -12
subl $8, %esp // NOTE prologue does not touch %ebp
.cfi_def_cfa_offset 20
#APP
# 235 "lib/bcheck.c" 1
movl %ebp,%edx // %ebp -> fp1
# 0 "" 2
#NO_APP
movl (%edx), %esi // fp1[0] -> fp
movl (%eax), %edx
movl %eax, %ebx
testl %edx, %edx
je .L167
.p2align 2,,3
.L173:
movl 4(%ebx), %eax
addl $8, %ebx
movl %eax, 4(%esp)
addl %esi, %edx
movl %edx, (%esp)
call __bound_new_region
movl (%ebx), %edx
testl %edx, %edx
jne .L173
.L167:
addl $8, %esp
.cfi_def_cfa_offset 12
popl %ebx
.cfi_restore 3
.cfi_def_cfa_offset 8
popl %esi
.cfi_restore 6
.cfi_def_cfa_offset 4
ret
here GET_CALLER_FP() assumed that its using function setups it's stack
frame, i.e. first save, then set %ebp to stack frame start, and then it
has to do perform two lookups: 1) to get current stack frame through
%ebp, and 2) get caller stack frame through (%ebp).
And here is the problem: gcc decided not to setup %ebp for
__bound_local_new and in such case GET_CALLER_FP actually becomes
GET_CALLER_CALLER_FP and oops, wrong regions are registered in bcheck
tables...
The solution is to stop using hand written assembly and rely on gcc's
__builtin_frame_address(1) to get callers frame stack(*). I think for the
builtin gcc should generate correct code, independent of whether it
decides or not to omit frame pointer in using function - it knows it.
(*) judging by gcc history, __builtin_frame_address was there almost
from the beginning - at least it is present in 1992 as seen from the
following commit:
http://gcc.gnu.org/git/?p=gcc.git;a=commit;h=be07f7bdbac76d87d3006c89855491504d5d6202
so we can rely on it being supported by all versions of gcc.
In my environment the assembly of __bound_local_new changes as follows:
diff --git a/bcheck0.s b/bcheck1.s
index 4c02a5f..ef68918 100644
--- a/bcheck0.s
+++ b/bcheck1.s
@@ -1409,20 +1409,17 @@ __bound_init:
__bound_local_new:
.LFB40:
.cfi_startproc
- pushl %esi
+ pushl %ebp // NOTE prologue saves %ebp ...
.cfi_def_cfa_offset 8
- .cfi_offset 6, -8
+ .cfi_offset 5, -8
+ movl %esp, %ebp // ... and reset it to local stack frame
+ .cfi_def_cfa_register 5
+ pushl %esi
pushl %ebx
- .cfi_def_cfa_offset 12
- .cfi_offset 3, -12
subl $8, %esp
- .cfi_def_cfa_offset 20
-#APP
-# 235 "lib/bcheck.c" 1
- movl %ebp,%edx
-# 0 "" 2
-#NO_APP
- movl (%edx), %esi
+ .cfi_offset 6, -12
+ .cfi_offset 3, -16
+ movl 0(%ebp), %esi // stkframe -> stkframe.parent -> fp
movl (%eax), %edx
movl %eax, %ebx
testl %edx, %edx
@@ -1440,13 +1437,13 @@ __bound_local_new:
jne .L173
.L167:
addl $8, %esp
- .cfi_def_cfa_offset 12
popl %ebx
.cfi_restore 3
- .cfi_def_cfa_offset 8
popl %esi
.cfi_restore 6
- .cfi_def_cfa_offset 4
+ popl %ebp
+ .cfi_restore 5
+ .cfi_def_cfa 4, 4
ret
.cfi_endproc
i.e. now it compiles correctly.
Though I do not have x86_64 to test, my guess is that
__builtin_frame_address(1) should work there too. If not - please revert
only x86_64 part of the patch. Thanks.
Cc: Michael Matz <matz@suse.de>
|
||
|---|---|---|
| examples | ||
| include | ||
| lib | ||
| tests | ||
| tests2 | ||
| win32 | ||
| .gitignore | ||
| arm-gen.c | ||
| c67-gen.c | ||
| Changelog | ||
| coff.h | ||
| configure | ||
| COPYING | ||
| elf.h | ||
| i386-asm.c | ||
| i386-asm.h | ||
| i386-gen.c | ||
| i386-tok.h | ||
| il-gen.c | ||
| il-opcodes.h | ||
| libtcc.c | ||
| libtcc.h | ||
| Makefile | ||
| README | ||
| stab.def | ||
| stab.h | ||
| tcc-doc.texi | ||
| tcc.c | ||
| tcc.h | ||
| tccasm.c | ||
| tcccoff.c | ||
| tccelf.c | ||
| tccgen.c | ||
| tccpe.c | ||
| tccpp.c | ||
| tccrun.c | ||
| tcctok.h | ||
| texi2pod.pl | ||
| TODO | ||
| VERSION | ||
| x86_64-asm.h | ||
| x86_64-gen.c | ||
Tiny C Compiler - C Scripting Everywhere - The Smallest ANSI C compiler ----------------------------------------------------------------------- Features: -------- - SMALL! You can compile and execute C code everywhere, for example on rescue disks. - FAST! tcc generates optimized x86 code. No byte code overhead. Compile, assemble and link about 7 times faster than 'gcc -O0'. - UNLIMITED! Any C dynamic library can be used directly. TCC is heading torward full ISOC99 compliance. TCC can of course compile itself. - SAFE! tcc includes an optional memory and bound checker. Bound checked code can be mixed freely with standard code. - Compile and execute C source directly. No linking or assembly necessary. Full C preprocessor included. - C script supported : just add '#!/usr/local/bin/tcc -run' at the first line of your C source, and execute it directly from the command line. Documentation: ------------- 1) Installation on a i386 Linux host (for Windows read tcc-win32.txt) ./configure make make test make install By default, tcc is installed in /usr/local/bin. ./configure --help shows configuration options. 2) Introduction We assume here that you know ANSI C. Look at the example ex1.c to know what the programs look like. The include file <tcclib.h> can be used if you want a small basic libc include support (especially useful for floppy disks). Of course, you can also use standard headers, although they are slower to compile. You can begin your C script with '#!/usr/local/bin/tcc -run' on the first line and set its execute bits (chmod a+x your_script). Then, you can launch the C code as a shell or perl script :-) The command line arguments are put in 'argc' and 'argv' of the main functions, as in ANSI C. 3) Examples ex1.c: simplest example (hello world). Can also be launched directly as a script: './ex1.c'. ex2.c: more complicated example: find a number with the four operations given a list of numbers (benchmark). ex3.c: compute fibonacci numbers (benchmark). ex4.c: more complicated: X11 program. Very complicated test in fact because standard headers are being used ! As for ex1.c, can also be launched directly as a script: './ex4.c'. ex5.c: 'hello world' with standard glibc headers. tcc.c: TCC can of course compile itself. Used to check the code generator. tcctest.c: auto test for TCC which tests many subtle possible bugs. Used when doing 'make test'. 4) Full Documentation Please read tcc-doc.html to have all the features of TCC. Additional information is available for the Windows port in tcc-win32.txt. License: ------- TCC is distributed under the GNU Lesser General Public License (see COPYING file). Fabrice Bellard.