GCC with Bounds Checking, By Richard W.M. Jones <rwmj@doc.ic.ac.uk>
------------------------------------------------------------------

1. KNOWN BUGS
-------------

Description:
	Stuart Kemp <skemp@bmc.com> reported that the following code
	doesn't compile correctly.
Example code:
	void Try ()
	{
	  char nodes [] [20] = {"foo", "bar"};
	}
Comment:
	The code throws the error `empty initializer cannot be bounds
	checked'. Relevant code is in `c-bounds.c' line 530-ish.
	(16/1/96): I think this is fixed now.

--

Description:
	Presumably for the same reasons, the following code doesn't compile
	properly (throwing the same error):
Example Code:
	/* copy definition from sys/types.h */
	typedef	unsigned char	u_char;

	/* copy definition from if_ether.h */
	struct ether_addr {
	  u_char	ether_addr_octet[6];
	};

	main() {
	  struct ether_addr e = { 1, 2, 3, 4, 5, 6} ;
	}
Comment:
	(16/1/96): I think this is fixed now.


--

Description:
	Clean up code not correctly generated by C++ destructor mechanism
	for VAR DECLs and functions.
Example code:
	tclCmdIL.c:254: var. `search' not correctly destroyed when we leave
		the function at certain points (but at other points, it is
		destroyed correctly). I've managed to work around this
		particular case by inserting some gotos.
	tclUtil.c:827: `__bounds_pop_function' is not called at some exit
		points from the function, but it is at other exit points. Again
		by rearranging the gotos in this function I've managed to
		avoid the problem.
	tclParse.c:Tcl_ParseVar: (same as tclUtil.c:827)
	tkBind.c:tkBindEvent: (same as tclUtil.c:827)
Comment:
	This bug appeared when we moved to GCC 2.7.0. I'm really not sure
	if it's because of the new version of GCC, or if it's because I'm
	not making the destructor right.
	This only seems to occur (so far) in functions that contain wierd
	uses of `goto'.
	Surely someone using C++ would have noticed this bug with the new
	GCC by now?
Related files:
	The destructor mechanism is called in `c-bounds.c'.
Update:
	(14/8/95, Linux): This appears to work OK now.

--

Description:
	Aggregate followed by 32-bit object on the stack: The aggregate is not
	padded correctly.
Example code:
	f ()
	{
	  char a[10];
	  int i;

	  printf ("a = %p, &i = %p\n", a, &i);
	}
Comment:
	Aggregates followed by other aggregates _are_ padded correctly, so
	this bug is wierd.
	You can temporarily fix this by inserting a line like 'int DUMMY;'
	between 'a' and 'i' declarations. We need to find the bug.
Related files:
	The stack padding code is in 'stmt.c'.
Update:
	(18/8/95, Linux): I have recently recompiled Tk 4.0 and this bug has
	not reappeared. Whether it has truly disappeared, I cannot say.
	(10/9/95, Solaris): This bug seems to still appear on other platforms,
	eg. Solaris.
	(16/10/95): This may have been caused by the bigend_correction stuff
	in assign_stack_local and assign_outer_stack_local. I've changed this
	around a bit & it may work now on bigendian machines (eg. Suns).

--

Description:
	Frank Cringle <fdc@cliwe.ping.de> writes ...
	Another problem is summarised below:

        % cat test.c
        struct s {
            int foo, bar;
        };

        extern struct s *one;

        void f(void)
        {
            struct s two = *one;
        }

        % $CC -I/usr/openwin/include test.c
        test.c: In function `f':
        test.c:9: empty initializer cannot be bounds checked

	I do not see an easy way to change this source, especially as in the real
	program the external struct originates in a library (X11).
Comments:
	(16/1/96): I think this is fixed now.

--

Description:
	`char p[10] = "init";' and `char *p[] = {"a", "b", "c"};' not
	compiled correctly in functions.
Example code:
	void func1(void)
	{
	   char *charArray[] = {"one", "two", "three"};
	}
	void func2(void)
	{
	   char p[255] = "/path/";
	}
Comment:
	I'm using what I think is the equivalent of `sizeof' to find
	the size of these objects. Now if you actually use C's `sizeof'
	here it works OK. If I use the equivalent `c_sizeof_nowarn'
	function in GCC, it returns 0. Why?
Related files:
	See `c-bounds.c' around lines 310-320 for a comment.
Update:
	(14/8/95, Linux): `func2' now compiles OK.
	(17/8/95, Linux): `func1' OK - thanks to H-t-B for his patch.

--

Description:
	Frank Cringle <fdc@cliwe.ping.de> writes ...
Here is a situation where linking fails (only) if -fbounds-checking is used.
Note that the variable 'verbose' gets assigned to a 2 byte common block in
bug1.o, but it is defined as 1 byte in bug2.o.  The linker does not like that.

 % uname -a
 SunOS cliwe 5.2 Generic sun4m sparc
 % $CC -v
 Reading specs from /opt/gnu/src/gnu/gcc-2.6.3/specs
 gcc version 2.6.3
 % cat bug1.c
 char verbose;

 int
 main(int argc, char **argv)
 {
     return verbose;
 }

 % cat bug2.c
 char verbose = 0;

 % nm *.o

 Symbols from bug1.o:
 
 [Index]   Value      Size    Type  Bind  Other Shndx   Name
 
 [1]     |         0|       0|FILE |LOCL |0    |ABS    |bug1.c
 [2]     |        32|       0|NOTY |LOCL |0    |3      |__bounds_private_statics
 [3]     |         0|       0|SECT |LOCL |0    |4      |
 [4]     |         0|       0|SECT |LOCL |0    |3      |
 [5]     |         0|       0|NOTY |LOCL |0    |2      |gcc2_compiled.
 [6]     |         0|       0|SECT |LOCL |0    |2      |
 [7]     |       184|      92|FUNC |GLOB |0    |2      |_GLOBAL_.I.main
 [8]     |         0|       0|NOTY |GLOB |0    |UNDEF  |__bounds_note_constructe
d_private_table
 [9]     |         0|       0|NOTY |GLOB |0    |UNDEF  |__bounds_pop_function
 [10]    |         0|       0|NOTY |GLOB |0    |UNDEF  |__bounds_note_constructe
d_object
 [11]    |         0|       0|NOTY |GLOB |0    |UNDEF  |__bounds_add_param_objec
t
 [12]    |         1|       2|OBJT |GLOB |0    |COMMON |verbose
 [13]    |         0|       0|NOTY |GLOB |0    |UNDEF  |__bounds_push_function
 [14]    |         0|     184|FUNC |GLOB |0    |2      |main
 [15]    |         0|       0|NOTY |GLOB |0    |UNDEF  |__bounds_initialize_libr
ary
 
 
 Symbols from bug2.o:
 
 [Index]   Value      Size    Type  Bind  Other Shndx   Name
 
 [1]     |         0|       0|FILE |LOCL |0    |ABS    |bug2.c
 [2]     |         0|       0|NOTY |LOCL |0    |4      |__bounds_private_statics
 [3]     |         0|       0|SECT |LOCL |0    |5      |
 [4]     |         0|       0|SECT |LOCL |0    |4      |
 [5]     |         0|       0|NOTY |LOCL |0    |2      |gcc2_compiled.
 [6]     |         0|       0|SECT |LOCL |0    |3      |
 [7]     |         0|       0|SECT |LOCL |0    |2      |
 [8]     |         0|       0|NOTY |GLOB |0    |UNDEF  |__bounds_note_constructe
d_private_table
 [9]     |         0|       0|NOTY |GLOB |0    |UNDEF  |__bounds_note_constructe
d_object
 [10]    |         0|       1|OBJT |GLOB |0    |3      |verbose
 [11]    |         0|      92|FUNC |GLOB |0    |2      |_GLOBAL_.I.verbose
 [12]    |         0|       0|NOTY |GLOB |0    |UNDEF  |__bounds_initialize_libr
ary

Comments:
	Placing `extern' before the uninitialized declarations fixes this.
Update:
	(18/8/95): HtB has patched `varasm.c' to fix this one, but I need
	someone to test it on a Sun-4 machine.

--

Description:
	Frank Cringle <fdc@cliwe.ping.de> writes ...
Here is a bug that reports a supposed bounds error, but only when compiled
with optimization.

 % uname -a
 SunOS cliwe 5.2 Generic sun4m sparc
 % $CC -v
 Reading specs from /opt/gnu/src/gnu/gcc-2.6.3/specs
 gcc version 2.6.3
 % cat bug.c
 typedef long    uid_t;
 typedef uid_t   gid_t;

 void
 g(uid_t *uid, gid_t *gid)
 {
     *uid = 0;
     *gid = 0;
 }

 int
 main(int argc, char **argv)
 {
     uid_t uid;
     uid_t gid;

     g(&uid, &gid);
     return uid+gid;
 }
 %$CC -g -O bug.c
 %a.out
In file bug.c, line 7,
  Bounds error: attempt to reference memory overrunning the end of an object.
  Pointer value: 0xeffff9d4
  Object `gid':
    Address in memory:    0xeffff9d0 .. 0xeffff9d3
    Size:                 4 bytes
    Element size:         4 bytes
    Number of elements:   1
    Created at:           bug.c, line 14
    Storage class:        stack

--

Description:
	The following bug was pointed out by Eberhard Mattes
	<mattes@azu.informatik.uni-stuttgart.d400.de>
Example code:
	#define FOO(c) ({ int temp = c; temp; })

	static void foo (int x)
	{
	  goto lbl;
	
	lbl:
	  if (FOO (x))
	    ++x;
	}
Comment:
	Without -fbounds-checking, no error.
	With -fbounds-checking, we get:
		foo.c: In function `foo':
		foo.c:7: label `lbl' used before containing binding contour
	Must be something to do with how I'm adding constructors to
	variables in binding contours.


2. MISSING FEATURES
-------------------

Description:
	Function pointers aren't found & checked.
Comment:
	We can find all function pointers using the same mechanism as for
	static variables. We make them into byte-sized objects with
	alignment -1 so that you can't do pointer arithmetic on them.


3. LIMITATIONS IN FUNCTIONALITY
-------------------------------

Description:
	Bounds checking isn't compatible with setjmp, longjmp.
Comment:
	The mechanism used by push/pop function (see 'functions.c' in the
	bc-library) doesn't like functions that don't return.
