This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Object size checking to prevent (some) buffer overflows


Hi!

The attached patch (which relies on
http://gcc.gnu.org/ml/gcc-patches/2004-09/msg01993.html
) provides a lightweight buffer overflow protection to
some memory and string functions.
I'm well aware of mudflap, but it has too big runtime overhead
to be used by all programs.  The intent of this patch
is to add some checks that have no or non-measurable
runtime overhead, so something that can be enabled for
all programs and libraries in an operating system.
The patch certainly doesn't prevent all buffer overflows,
but should prevent many common ones.
It works by computing a constant (conservative) number
of bytes remaining to the end of object(s) each destination
pointer passed to memory and string functions, if possible
checking for overflows at compile time, if not possible
passing that constant size to special checking alternatives
of the memory/string functions.

http://people.redhat.com/jakub/glibc-chk.patch
contains the glibc counterpart to this patch, although
it doesn't have to be glibc/Linux specific at all.
One could easily take bits/string3.h and part of bits/stdio2.h
from the above patch, rename to <string.h> resp. <stdio.h>,
#include_next <string.h>/<stdio.h> in these headers and
create a library containing the checking functions.

The intended use in glibc is that by default no protection is
done, when the above GCC 4.0+ and -D_FORTIFY_SOURCE=1 is used
at optimization level 1 and above, security measures that
shouldn't change behaviour of conforming programs are taken.
With -D_FORTIFY_SOURCE=2 some more checking is added, but
some conforming programs might fail.

Buffer overflows can be detected at compile time
or at runtime, if the compiler can detect they will not
happen, normal functions as opposed to their checking
alternatives are used.

Below are four different cases that can happen:

char buf[5];
/* 1) Known correct.
      No runtime checking is needed, memcpy/strcpy
      functions are called (or their equivalents inline).  */
memcpy (buf, foo, 5);
strcpy (buf, "abcd");
/* 2) Not known if correct, but checkable at runtime.
      The compiler knows the number of bytes remaining in object,
      but doesn't know the length of the actual copy that will happen.
      Alternative functions __memcpy_chk or __strcpy_chk are used in
      this case that check whether buffer overflow happened.  If buffer
      overflow is detected, __chk_fail () is called (the normal action
      is to abort () the application, perhaps by writing some message
      to stderr.  */
memcpy (buf, foo, n);
strcpy (buf, bar);
/* 3) Known incorrect.
      The compiler can detect buffer overflows at compile
      time.  It issues warnings and calls the checking alternatives
      at runtime.  */
memcpy (buf, foo, 6);
strcpy (buf, "abcde");
/* 4) Not known if correct, not checkable at runtime.
      The compiler doesn't know the buffer size, no checking
      is done.  Overflows will go undetected in these cases.  */
memcpy (p, q, n);
strcpy (p, q);

In the current implementation mem{cpy,pcpy,move,set},
st{r,p,nc}py, str{,n}cat, {,v}s{,n}printf and gets functions
are checked this way.

The diffence between -D_FORTIFY_SOURCE=1 and -D_FORTIFY_SOURCE=2
is e.g. for
struct S { struct T { char buf[5]; int x; } t; char buf[20]; } var;
With -D_FORTIFY_SOURCE=1,
strcpy (&var.t.buf[1], "abcdefg");
is not considered an overflow (object is whole VAR), while
with -D_FORTIFY_SOURCE=2
strcpy (&var.t.buf[1], "abcdefg");
will be considered a buffer overflow.

Another difference is that with -D_FORTIFY_SOURCE=2, %n
in format strings of the most common *printf family functions
is allowed only if it is stored in read-only memory (usually
string literals, gettext's _("%s string %n") is fine too), but
usually when an attacker attempts to exploit a format string
vulnerability, %n will be somewhere where the attacker could
write it into.

Now about how is this implemented in GCC.
The patch introduces a
size_t __builtin_object_size (ptr, subobject_p)
builtin which computes the number of remaining bytes from PTR
till the end of the object PTR is pointing to.
If there are multiple objects PTR can point to, this builtin
returns maximum number of remaining bytes for them if all
the objects are known at compile time.  If it is unknown
what PTR points to, this builtin returns (size_t) -1.
SUBOBJECT_P is a boolean, 0 if whole objects are considered
to be the objects for the remaining bytes computations, if
it is 1, it means the innermost structure field or array
is considered to be the object.

The largest part of the patch is addition of many __*_chk builtins,
checking alternatives to normal string and memory operations.
These builtins are optimized into normal string/memory operations
if compiler can prove there will be no buffer overflow or if
the number of remaining bytes destination pointer points to is
unknown.  The builtins issue a warning (some people say these
should be errors) if at compile time the compiler can prove
there will be buffer overflow if the function is called.
Otherwise the builtins expand to calls to the __*_chk functions,
with a constant object size argument.

For routines like mem{cpy,pcpy,move,set},strncpy etc. the runtime
overhead of the checking functions is just a few ticks (comparison
of two arguments, non-predicted branch to __chk_fail function),
for strcpy/stpcpy/strcat/strncat the runtime overhead is slightly
bigger, but as long as they are coded in assembly like the normal
routines, it shouldn't be that big.  All functions in the glibc
implementation check for overflows before they happen, but it would
be possible to only detect they happened and make e.g. __strcpy_chk
faster.

Ok, actually the largest part of the patch is testsuite, there is
160k of tests for the added builtins.

Arjan van de Ven built some packages from Fedora Core 3 test 2
distribution with GCC that included this patch, glibc with the patch
mentioned above and -D_FORTIFY_SOURCE=1 and it detected several
buffer overflows at compile time, including e.g.

char *devstr, dest;
strncpy(&dest, (optarg + 1), 3);

struct XXXX *rule;
if (!(rule = malloc(sizeof(rule)))) return NULL;
memset(rule, 0, sizeof(*rule));

typedef struct {
char hash[16];
HASH;
const char *XXXX(const HASH hash) {
unsigned int i;
memcpy(&i, &hash, sizeof(hash));

XXX *sheets[3];
memset (sheets, 0, sizeof (XXX) * 3);

#define strmov(A,B) stpcpy((A),(B))
struct XXXX {
char id[8];
char nr[4];
char text[10];
} YYYY;
strmov(YYYY.text,"Testing...");

Thanks to Arjan van de Ven and Ulrich Drepper for coming up with the idea,
Ulrich for the glibc side of things, Diego Novillo for helping
me with tree-SSA stuff and to Arjan for testing.

2004-09-20  Jakub Jelinek  <jakub@redhat.com>

	* builtin-attrs.def (DEF_ATTR_FOR_INT): Add for 5 and 6.
	(DEF_LIST_INT_INT): Add for 4,0, 4,5, 5,0, 5,6.
	(ATTR_NOTHROW_NONNULL_4, ATTR_NOTHROW_NONNULL_5): Define.
	(ATTR_FORMAT_PRINTF_4_0, ATTR_FORMAT_PRINTF_4_5,
	ATTR_FORMAT_PRINTF_5_0, ATTR_FORMAT_PRINTF_5_6): Define.
	* builtins.c: Include tree-flow.h.
	(expand_builtin_mempcpy, expand_builtin_memmove,
	expand_builtin_strncat): Comment fixes, formatting.
	(expand_builtin_memory_chk, maybe_emit_chk_warning,
	maybe_emit_sprintf_chk_warning, compute_object_offset,
	compute_builtin_object_size, fold_builtin_object_size): New
	functions.
	(expand_builtin): Handle BUILT_IN_OBJECT_SIZE and BUILT_IN_*_CHK.
	(fold_builtin_1): Likewise.  Handle BUILT_IN_{,V}{,F}PRINTF
	and BUILT_IN_{,F}PRINTF_UNLOCKED.
	(fold_builtin_memory_chk, fold_builtin_stxcpy_chk,
	fold_builtin_strncpy_chk, fold_builtin_strcat_chk,
	fold_builtin_strncat_chk, fold_builtin_sprintf_chk,
	fold_builtin_snprintf_chk, fold_builtin_printf, fold_builtin_fprintf):
	New functions.
	* builtins.def (BUILT_IN_OBJECT_SIZE, BUILT_IN_MEMCPY_CHK,
	BUILT_IN_MEMMOVE_CHK, BUILT_IN_MEMPCPY_CHK, BUILT_IN_MEMSET_CHK,
	BUILT_IN_STPCPY_CHK, BUILT_IN_STRCAT_CHK, BUILT_IN_STRCPY_CHK,
	BUILT_IN_STRNCAT_CHK, BUILT_IN_STRNCPY_CHK, BUILT_IN_SNPRINTF_CHK,
	BUILT_IN_SPRINTF_CHK, BUILT_IN_VSNPRINTF_CHK, BUILT_IN_VSPRINTF_CHK,
	BUILT_IN_FPRINTF_CHK, BUILT_IN_PRINTF_CHK, BUILT_IN_VFPRINTF_CHK,
	BUILT_IN_VPRINTF_CHK): New builtins.
	* builtin-types.def (DEF_FUNCTION_TYPE_5, DEF_FUNCTION_TYPE_VAR_4):
	Document.
	(BT_FN_SIZE_CONST_PTR_INT, BT_FN_INT_INT_CONST_STRING_VALIST_ARG,
	BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, BT_FN_PTR_PTR_INT_SIZE_SIZE,
	BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE,
	BT_FN_INT_FILEPTR_INT_CONST_STRING_VALIST_ARG,
	BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VALIST_ARG,
	BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VALIST_ARG,
	BT_FN_INT_INT_CONST_STRING_VAR, BT_FN_INT_FILEPTR_INT_CONST_STRING_VAR,
	BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VAR,
	BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VAR): New types.
	* c-common.c (DEF_FUNCTION_TYPE_5, DEF_FUNCTION_TYPE_6,
	DEF_FUNCTION_TYPE_VAR_4, DEF_FUNCTION_TYPE_VAR_5): Define.
	* Makefile.in (builtins.o): Depend on $(TREE_FLOW_H).
	* tree-flow.h (struct ptr_info_def): Add pt_object_size and
	pt_subobject_size fields.
	* tree.h (fold_builtin_memory_chk, fold_builtin_stxcpy_chk,
	fold_builtin_strncpy_chk, fold_builtin_snprintf_chk,
	compute_builtin_object_size): New prototypes.
	* tree-ssa-alias.c (init_alias_info, merge_pointed_to_info,
	add_pointed_to_expr, add_pointed_to_var): Compute pt_object_size
	and pt_subobject_size.
	(add_pointed_to_plus_expr): New function, split from ...
	(collect_points_to_info_r): ...here.
	(dump_points_to_info_for): Dump pt_object_size and pt_subobject_size.
	* tree-ssa-ccp.c (get_strlen): Rename to ...
	(get_maxval_strlen): ...this function.  Handle also computing of maximum
	string length and maximum integral value.
	(ccp_fold_builtin): Handle BUILT_IN_*_CHK.  Use get_maxval_strlen
	instead of get_strlen.
	(execute_fold_all_builtins): Retry ccp_fold_builtin if a builtin changed
	into some other builtin.
	* doc/extend.texi (Object Size Checking): Document.

	* gcc.c-torture/execute/builtins/lib/main.c (abort): Add prototype.
	* gcc.c-torture/execute/builtins/lib/strncat.c (strncat): Avoid
	testing uninitialized var.

	* gcc.c-torture/execute/builtins/chk.h: New.
	* gcc.c-torture/execute/builtins/lib/chk.c: New.
	* gcc.c-torture/execute/builtins/memcpy-chk.c: New test.
	* gcc.c-torture/execute/builtins/memcpy-chk-lib.c: New.
	* gcc.c-torture/execute/builtins/memmove-chk.c: New test.
	* gcc.c-torture/execute/builtins/memmove-chk-lib.c: New.
	* gcc.c-torture/execute/builtins/mempcpy-chk.c: New test.
	* gcc.c-torture/execute/builtins/mempcpy-chk-lib.c: New.
	* gcc.c-torture/execute/builtins/memset-chk.c: New test.
	* gcc.c-torture/execute/builtins/memset-chk-lib.c: New.
	* gcc.c-torture/execute/builtins/snprintf-chk.c: New test.
	* gcc.c-torture/execute/builtins/snprintf-chk-lib.c: New.
	* gcc.c-torture/execute/builtins/sprintf-chk.c: New test.
	* gcc.c-torture/execute/builtins/sprintf-chk-lib.c: New.
	* gcc.c-torture/execute/builtins/stpcpy-chk.c: New test.
	* gcc.c-torture/execute/builtins/stpcpy-chk-lib.c: New.
	* gcc.c-torture/execute/builtins/strcat-chk.c: New test.
	* gcc.c-torture/execute/builtins/strcat-chk-lib.c: New.
	* gcc.c-torture/execute/builtins/strcpy-chk.c: New test.
	* gcc.c-torture/execute/builtins/strcpy-chk-lib.c: New.
	* gcc.c-torture/execute/builtins/strncat-chk.c: New test.
	* gcc.c-torture/execute/builtins/strncat-chk-lib.c: New.
	* gcc.c-torture/execute/builtins/strncpy-chk.c: New test.
	* gcc.c-torture/execute/builtins/strncpy-chk-lib.c: New.
	* gcc.c-torture/execute/builtins/vsnprintf-chk.c: New test.
	* gcc.c-torture/execute/builtins/vsnprintf-chk-lib.c: New.
	* gcc.c-torture/execute/builtins/vsprintf-chk.c: New test.
	* gcc.c-torture/execute/builtins/vsprintf-chk-lib.c: New.
	* gcc.dg/builtin-object-size-1.c: New test.
	* gcc.dg/builtin-object-size-2.c: New test.
	* gcc.dg/builtin-stringop-chk-1.c: New test.
	* gcc.dg/tree-ssa/builtin-fprintf-1.c: New test.
	* gcc.dg/tree-ssa/builtin-fprintf-chk-1.c: New test.
	* gcc.dg/tree-ssa/builtin-printf-1.c: New test.
	* gcc.dg/tree-ssa/builtin-printf-chk-1.c: New test.
	* gcc.dg/tree-ssa/builtin-vfprintf-1.c: New test.
	* gcc.dg/tree-ssa/builtin-vfprintf-chk-1.c: New test.
	* gcc.dg/tree-ssa/builtin-vprintf-1.c: New test.
	* gcc.dg/tree-ssa/builtin-vprintf-chk-1.c: New test.

--- gcc/builtin-attrs.def.jj	2004-09-11 20:34:49.000000000 +0200
+++ gcc/builtin-attrs.def	2004-09-20 18:09:25.224576716 +0200
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+/* Copyright (C) 2001, 2002, 2004 Free Software Foundation, Inc.
    Contributed by Joseph Myers <jsm28@cam.ac.uk>.
 
 This file is part of GCC.
@@ -55,6 +55,8 @@ DEF_ATTR_FOR_INT (1)
 DEF_ATTR_FOR_INT (2)
 DEF_ATTR_FOR_INT (3)
 DEF_ATTR_FOR_INT (4)
+DEF_ATTR_FOR_INT (5)
+DEF_ATTR_FOR_INT (6)
 #undef DEF_ATTR_FOR_INT
 
 /* Construct a tree for a list of two integers.  */
@@ -67,6 +69,10 @@ DEF_LIST_INT_INT (2,0)
 DEF_LIST_INT_INT (2,3)
 DEF_LIST_INT_INT (3,0)
 DEF_LIST_INT_INT (3,4)
+DEF_LIST_INT_INT (4,0)
+DEF_LIST_INT_INT (4,5)
+DEF_LIST_INT_INT (5,0)
+DEF_LIST_INT_INT (5,6)
 #undef DEF_LIST_INT_INT
 
 /* Construct trees for identifiers.  */
@@ -109,6 +115,10 @@ DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL
 			ATTR_NOTHROW_LIST)
 DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_3, ATTR_NONNULL, ATTR_LIST_3, \
 			ATTR_NOTHROW_LIST)
+DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_4, ATTR_NONNULL, ATTR_LIST_4, \
+			ATTR_NOTHROW_LIST)
+DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_5, ATTR_NONNULL, ATTR_LIST_5, \
+			ATTR_NOTHROW_LIST)
 /* Nothrow functions whose first and second parameters are nonnull pointers.  */
 DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_1_2, ATTR_NONNULL, ATTR_LIST_2, \
 			ATTR_NOTHROW_NONNULL_1)
@@ -140,6 +150,10 @@ DEF_FORMAT_ATTRIBUTE(PRINTF,2,2_0)
 DEF_FORMAT_ATTRIBUTE(PRINTF,2,2_3)
 DEF_FORMAT_ATTRIBUTE(PRINTF,3,3_0)
 DEF_FORMAT_ATTRIBUTE(PRINTF,3,3_4)
+DEF_FORMAT_ATTRIBUTE(PRINTF,4,4_0)
+DEF_FORMAT_ATTRIBUTE(PRINTF,4,4_5)
+DEF_FORMAT_ATTRIBUTE(PRINTF,5,5_0)
+DEF_FORMAT_ATTRIBUTE(PRINTF,5,5_6)
 DEF_FORMAT_ATTRIBUTE(SCANF,1,1_0)
 DEF_FORMAT_ATTRIBUTE(SCANF,1,1_2)
 DEF_FORMAT_ATTRIBUTE(SCANF,2,2_0)
--- gcc/tree-ssa-alias.c.jj	2004-09-20 17:35:57.476660206 +0200
+++ gcc/tree-ssa-alias.c	2004-09-20 18:09:25.206579901 +0200
@@ -432,6 +432,8 @@ init_alias_info (void)
 	      pi->is_dereferenced = 0;
 	      if (pi->pt_vars)
 		bitmap_clear (pi->pt_vars);
+	      pi->pt_object_size = 0;
+	      pi->pt_subobject_size = 0;
 	    }
 	}
     }
@@ -1732,9 +1734,18 @@ merge_pointed_to_info (struct alias_info
 		           dest_pi->pt_vars,
 		           orig_pi->pt_vars);
 	}
+
+      if (orig_pi->pt_object_size > dest_pi->pt_object_size)
+	dest_pi->pt_object_size = orig_pi->pt_object_size;
+      if (orig_pi->pt_subobject_size > dest_pi->pt_subobject_size)
+	dest_pi->pt_subobject_size = orig_pi->pt_subobject_size;
     }
   else
-    set_pt_anything (dest);
+    {
+      set_pt_anything (dest);
+      dest_pi->pt_object_size = (unsigned HOST_WIDE_INT) -1;
+      dest_pi->pt_subobject_size = (unsigned HOST_WIDE_INT) -1;
+    }
 }
 
 
@@ -1743,6 +1754,8 @@ merge_pointed_to_info (struct alias_info
 static void
 add_pointed_to_expr (tree ptr, tree value)
 {
+  struct ptr_info_def *pi;
+
   if (TREE_CODE (value) == WITH_SIZE_EXPR)
     value = TREE_OPERAND (value, 0);
 
@@ -1750,15 +1763,33 @@ add_pointed_to_expr (tree ptr, tree valu
   gcc_assert (TREE_CODE (value) != SSA_NAME
 	      || !POINTER_TYPE_P (TREE_TYPE (value)));
 
-  get_ptr_info (ptr);
+  pi = get_ptr_info (ptr);
 
   /* If VALUE is the result of a malloc-like call, then the area pointed to
      PTR is guaranteed to not alias with anything else.  */
-  if (TREE_CODE (value) == CALL_EXPR
-      && (call_expr_flags (value) & (ECF_MALLOC | ECF_MAY_BE_ALLOCA)))
-    set_pt_malloc (ptr);
+  if (TREE_CODE (value) == CALL_EXPR)
+    {
+      unsigned HOST_WIDE_INT bytes;
+
+      if ((call_expr_flags (value) & (ECF_MALLOC | ECF_MAY_BE_ALLOCA)))
+	set_pt_malloc (ptr);
+      else
+	set_pt_anything (ptr);
+
+      bytes = compute_builtin_object_size (value, 0);
+      if (pi->pt_object_size < bytes)
+	pi->pt_object_size = bytes;
+
+      bytes = compute_builtin_object_size (value, 1);
+      if (pi->pt_subobject_size < bytes)
+	pi->pt_subobject_size = bytes;
+    }
   else
-    set_pt_anything (ptr);
+    {
+      set_pt_anything (ptr);
+      pi->pt_object_size = (unsigned HOST_WIDE_INT) -1;
+      pi->pt_subobject_size = (unsigned HOST_WIDE_INT) -1;
+    }	
 
   if (dump_file)
     {
@@ -1796,6 +1827,8 @@ add_pointed_to_var (struct alias_info *a
 
   if (pt_var && SSA_VAR_P (pt_var))
     {
+      unsigned HOST_WIDE_INT bytes;
+
       uid = var_ann (pt_var)->uid;
       bitmap_set_bit (ai->addresses_needed, uid);
 
@@ -1807,7 +1840,121 @@ add_pointed_to_var (struct alias_info *a
 	 global memory (which will make its tag a global variable).  */
       if (is_global_var (pt_var))
 	pi->pt_global_mem = 1;
+
+      bytes = compute_builtin_object_size (value, 0);
+      if (pi->pt_object_size < bytes)
+        pi->pt_object_size = bytes;
+
+      bytes = compute_builtin_object_size (value, 1);
+      if (pi->pt_subobject_size < bytes)
+        pi->pt_subobject_size = bytes;
+    }
+  else
+    add_pointed_to_expr (ptr, value);
+}
+
+
+/* Add VALUE, a PLUS_EXPR or MINUS_EXPR, to the list of expressions pointed-to
+   by VAR.  AI is as in collect_points_to_info.  */
+
+static void
+add_pointed_to_plus_expr (struct alias_info *ai, tree var, tree value)
+{
+  tree op0 = TREE_OPERAND (value, 0);
+  tree op1 = TREE_OPERAND (value, 1);
+  bool ptr1_p = POINTER_TYPE_P (TREE_TYPE (op0))
+		&& TREE_CODE (op0) != INTEGER_CST;
+  bool ptr2_p = POINTER_TYPE_P (TREE_TYPE (op1))
+		&& TREE_CODE (op1) != INTEGER_CST;
+  struct ptr_info_def *pi = get_ptr_info (var);
+  unsigned HOST_WIDE_INT saved_object_size = pi->pt_object_size;
+  unsigned HOST_WIDE_INT saved_subobject_size = pi->pt_subobject_size;
+
+  /* Swap operands if needed.  */
+  if (ptr2_p && !ptr1_p)
+    {
+      tree tem = op0;
+      op0 = op1;
+      op1 = tem;
+      ptr1_p = true;
+      ptr2_p = false;
+    }
+
+  /* For PTR + OFFSET, we'll do object size merging later.
+     Otherwise assume we don't know anything about the object
+     size.  */
+  if (ptr1_p
+      && !ptr2_p
+      && TREE_CODE (op1) == INTEGER_CST
+      && TREE_CODE (value) == PLUS_EXPR)
+    {
+      pi->pt_object_size = 0;
+      pi->pt_subobject_size = 0;
+    }
+  else
+    {
+      pi->pt_object_size = (unsigned HOST_WIDE_INT) -1;
+      pi->pt_subobject_size = (unsigned HOST_WIDE_INT) -1;
     }
+ 
+   /* FIXME: Shouldn't we just expect PTR + OFFSET always?  */
+  if (ptr1_p)
+    {
+      if (TREE_CODE (op0) == SSA_NAME)
+	merge_pointed_to_info (ai, var, op0);
+      else if (TREE_CODE (op0) == ADDR_EXPR)
+	add_pointed_to_var (ai, var, op0);
+      else
+	add_pointed_to_expr (var, op0);
+    }
+
+  if (ptr2_p)
+    {
+      if (TREE_CODE (op1) == SSA_NAME)
+	merge_pointed_to_info (ai, var, op1);
+      else if (TREE_CODE (op1) == ADDR_EXPR)
+	add_pointed_to_var (ai, var, op1);
+      else
+	add_pointed_to_expr (var, op1);
+    }
+
+  if (ptr1_p
+      && !ptr2_p
+      && TREE_CODE (op1) == INTEGER_CST
+      && TREE_CODE (value) == PLUS_EXPR)
+    {
+      unsigned HOST_WIDE_INT off = 0;
+
+      if (pi->pt_object_size == (unsigned HOST_WIDE_INT) -1)
+	;
+      else if (! host_integerp (op1, 1)
+	       || (off = tree_low_cst (op1, 1)) > pi->pt_object_size)
+	pi->pt_object_size = (unsigned HOST_WIDE_INT) -1;
+      else
+	{
+	  pi->pt_object_size -= off;
+	  if (pi->pt_object_size < saved_object_size)
+	    pi->pt_object_size = saved_object_size;
+	}
+
+      if (pi->pt_subobject_size == (unsigned HOST_WIDE_INT) -1)
+	;
+      else if (! host_integerp (op1, 1)
+	       || (off = tree_low_cst (op1, 1)) > pi->pt_subobject_size)
+	pi->pt_subobject_size = (unsigned HOST_WIDE_INT) -1;
+      else
+	{
+	  pi->pt_subobject_size -= off;
+	  if (pi->pt_subobject_size < saved_subobject_size)
+	    pi->pt_subobject_size = saved_subobject_size;
+	}
+    }
+
+  /* Neither operand is a pointer?  VAR can be pointing anywhere.
+     FIXME: Is this right?  If we get here, we found
+     PTR = INT_CST + INT_CST.  */
+  else if (!ptr1_p && !ptr2_p)
+    add_pointed_to_expr (var, value);
 }
 
 
@@ -1858,43 +2005,7 @@ collect_points_to_info_r (tree var, tree
 	/* Found P_i = PLUS_EXPR or P_i = MINUS_EXPR  */
 	else if (TREE_CODE (rhs) == PLUS_EXPR
 		 || TREE_CODE (rhs) == MINUS_EXPR)
-	  {
-	    tree op0 = TREE_OPERAND (rhs, 0);
-	    tree op1 = TREE_OPERAND (rhs, 1);
-	    
-	    /* Both operands may be of pointer type.  FIXME: Shouldn't
-	       we just expect PTR + OFFSET always?  */
-	    if (POINTER_TYPE_P (TREE_TYPE (op0))
-		&& TREE_CODE (op0) != INTEGER_CST)
-	      {
-		if (TREE_CODE (op0) == SSA_NAME)
-		  merge_pointed_to_info (ai, var, op0);
-		else if (TREE_CODE (op0) == ADDR_EXPR)
-		  add_pointed_to_var (ai, var, op0);
-		else
-		  add_pointed_to_expr (var, op0);
-	      }
-
-	    if (POINTER_TYPE_P (TREE_TYPE (op1))
-		&& TREE_CODE (op1) != INTEGER_CST)
-	      {
-		if (TREE_CODE (op1) == SSA_NAME)
-		  merge_pointed_to_info (ai, var, op1);
-		else if (TREE_CODE (op1) == ADDR_EXPR)
-		  add_pointed_to_var (ai, var, op1);
-		else
-		  add_pointed_to_expr (var, op1);
-	      }
-
-	    /* Neither operand is a pointer?  VAR can be pointing
-	       anywhere.  FIXME: Is this right?  If we get here, we
-	       found PTR = INT_CST + INT_CST.  */
-	    if (!(POINTER_TYPE_P (TREE_TYPE (op0))
-		  && TREE_CODE (op0) != INTEGER_CST)
-		&& !(POINTER_TYPE_P (TREE_TYPE (op1))
-		     && TREE_CODE (op1) != INTEGER_CST))
-	      add_pointed_to_expr (var, rhs);
-	  }
+	  add_pointed_to_plus_expr (ai, var, rhs);
 
 	/* Something else.  */
 	else
@@ -2321,6 +2432,15 @@ dump_points_to_info_for (FILE *file, tre
 	      });
 	  fprintf (file, "}");
 	}
+
+      if (pi->pt_object_size != (unsigned HOST_WIDE_INT) -1)
+        fprintf (file, ", object size " HOST_WIDE_INT_PRINT_DEC,
+		 pi->pt_object_size);
+
+      if (pi->pt_object_size != (unsigned HOST_WIDE_INT) -1
+	  && pi->pt_subobject_size != pi->pt_object_size)
+        fprintf (file, ", subobject size " HOST_WIDE_INT_PRINT_DEC,
+		 pi->pt_subobject_size);
     }
 
   fprintf (file, "\n");
--- gcc/tree.h.jj	2004-09-18 13:49:36.000000000 +0200
+++ gcc/tree.h	2004-09-20 18:09:25.194582025 +0200
@@ -3539,6 +3539,10 @@ extern tree fold_builtin (tree, bool);
 extern tree fold_builtin_fputs (tree, bool, bool, tree);
 extern tree fold_builtin_strcpy (tree, tree);
 extern tree fold_builtin_strncpy (tree, tree);
+extern tree fold_builtin_memory_chk (tree, tree, bool, enum built_in_function);
+extern tree fold_builtin_stxcpy_chk (tree, tree, bool, enum built_in_function);
+extern tree fold_builtin_strncpy_chk (tree, tree);
+extern tree fold_builtin_snprintf_chk (tree, tree, enum built_in_function);
 extern enum built_in_function builtin_mathfn_code (tree);
 extern tree build_function_call_expr (tree, tree);
 extern tree mathfn_built_in (tree, enum built_in_function fn);
@@ -3547,6 +3551,7 @@ extern tree simplify_builtin (tree, int)
 extern tree c_strlen (tree, int);
 extern tree std_gimplify_va_arg_expr (tree, tree, tree *, tree *);
 extern tree build_va_arg_indirect_ref (tree);
+extern unsigned HOST_WIDE_INT compute_builtin_object_size (tree, int);
 
 /* In convert.c */
 extern tree strip_float_extensions (tree);
--- gcc/builtins.c.jj	2004-09-20 17:35:56.609813829 +0200
+++ gcc/builtins.c	2004-09-20 18:45:44.018348014 +0200
@@ -47,6 +47,7 @@ Software Foundation, 59 Temple Place - S
 #include "langhooks.h"
 #include "basic-block.h"
 #include "tree-mudflap.h"
+#include "tree-flow.h"
 
 #define CALLED_AS_BUILT_IN(NODE) \
    (!strncmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__builtin_", 10))
@@ -114,6 +115,10 @@ static rtx expand_builtin_strcspn (tree,
 static rtx expand_builtin_memcpy (tree, rtx, enum machine_mode);
 static rtx expand_builtin_mempcpy (tree, rtx, enum machine_mode, int);
 static rtx expand_builtin_memmove (tree, rtx, enum machine_mode);
+static rtx expand_builtin_memory_chk (tree, rtx, enum machine_mode,
+				      enum built_in_function);
+static void maybe_emit_chk_warning (tree, enum built_in_function);
+static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function);
 static rtx expand_builtin_bcopy (tree);
 static rtx expand_builtin_strcpy (tree, rtx, enum machine_mode);
 static rtx expand_builtin_stpcpy (tree, rtx, enum machine_mode);
@@ -138,6 +143,13 @@ static rtx expand_builtin_sprintf (tree,
 static tree stabilize_va_list (tree, int);
 static rtx expand_builtin_expect (tree, rtx);
 static tree fold_builtin_constant_p (tree);
+static tree compute_object_offset (tree, tree);
+static tree fold_builtin_object_size (tree);
+static tree fold_builtin_strcat_chk (tree);
+static tree fold_builtin_strncat_chk (tree);
+static tree fold_builtin_sprintf_chk (tree, enum built_in_function);
+static tree fold_builtin_printf (tree, enum built_in_function);
+static tree fold_builtin_fprintf (tree, enum built_in_function);
 static tree fold_builtin_classify_type (tree);
 static tree fold_builtin_strlen (tree);
 static tree fold_builtin_inf (tree, int);
@@ -2767,7 +2779,7 @@ expand_builtin_memcpy (tree arglist, rtx
 }
 
 /* Expand a call to the mempcpy builtin, with arguments in ARGLIST.
-   Return 0 if we failed the caller should emit a normal call,
+   Return 0 if we failed; the caller should emit a normal call,
    otherwise try to get the result in TARGET, if convenient (and in
    mode MODE if that's convenient).  If ENDP is 0 return the
    destination pointer, if ENDP is 1 return the end pointer ala
@@ -2886,7 +2898,7 @@ expand_builtin_mempcpy (tree arglist, rt
 }
 
 /* Expand expression EXP, which is a call to the memmove builtin.  Return 0
-   if we failed the caller should emit a normal call.  */
+   if we failed; the caller should emit a normal call.  */
 
 static rtx
 expand_builtin_memmove (tree arglist, rtx target, enum machine_mode mode)
@@ -2955,6 +2967,260 @@ expand_builtin_memmove (tree arglist, rt
    }
 }
 
+/* Expand EXP, a call to the __mem{cpy,pcpy,move,set}_chk builtin.
+   FCODE is the BUILT_IN_* to use.
+   Return 0 if we failed; the caller should emit a normal call,
+   otherwise try to get the result in TARGET, if convenient (and in
+   mode MODE if that's convenient).  */
+
+static rtx
+expand_builtin_memory_chk (tree exp, rtx target, enum machine_mode mode,
+			   enum built_in_function fcode)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree dest, src, len, size;
+
+  if (!validate_arglist (arglist,
+			 POINTER_TYPE,
+			 fcode == BUILT_IN_MEMSET_CHK
+			 ? INTEGER_TYPE : POINTER_TYPE,
+			 INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return 0;
+
+  dest = TREE_VALUE (arglist);
+  src = TREE_VALUE (TREE_CHAIN (arglist));
+  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+  size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist))));
+
+  if (! host_integerp (size, 1))
+    return 0;
+
+  if (host_integerp (len, 1) || integer_all_onesp (size))
+    {
+      tree fn;
+
+      if (! integer_all_onesp (size) && tree_int_cst_lt (size, len))
+	{
+	  location_t locus = EXPR_LOCATION (exp);
+	  warning ("%Hcall to %D will always overflow destination buffer",
+		   &locus, get_callee_fndecl (exp));
+	  return 0;
+	}
+
+      arglist = build_tree_list (NULL_TREE, len);
+      arglist = tree_cons (NULL_TREE, src, arglist);
+      arglist = tree_cons (NULL_TREE, dest, arglist);
+
+      fn = NULL_TREE;
+      /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume
+	 mem{cpy,pcpy,move,set} is available.  */
+      switch (fcode)
+	{
+	case BUILT_IN_MEMCPY_CHK:
+	  fn = built_in_decls[BUILT_IN_MEMCPY];
+	  break;
+	case BUILT_IN_MEMPCPY_CHK:
+	  fn = built_in_decls[BUILT_IN_MEMPCPY];
+	  break;
+	case BUILT_IN_MEMMOVE_CHK:
+	  fn = built_in_decls[BUILT_IN_MEMMOVE];
+	  break;
+	case BUILT_IN_MEMSET_CHK:
+	  fn = built_in_decls[BUILT_IN_MEMSET];
+	  break;
+	default:
+	  break;
+	}
+
+      if (! fn)
+	return 0;
+
+      return expand_expr (build_function_call_expr (fn, arglist),
+			  target, mode, EXPAND_NORMAL);
+    }
+  else if (fcode == BUILT_IN_MEMSET_CHK)
+    return 0;
+  else
+    {
+      unsigned int dest_align
+	= get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
+
+      /* If DEST is not a pointer type, call the normal function.  */
+      if (dest_align == 0)
+	return 0;
+
+      /* If SRC and DEST are the same (and not volatile), do nothing.  */
+      if (operand_equal_p (src, dest, 0))
+	{
+	  tree expr;
+
+	  if (fcode != BUILT_IN_MEMPCPY_CHK)
+	    {
+	      /* Evaluate and ignore LEN in case it has side-effects.  */
+	      expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
+	      return expand_expr (dest, target, mode, EXPAND_NORMAL);
+	    }
+
+	  len = fold_convert (TREE_TYPE (dest), len);
+	  expr = fold (build2 (PLUS_EXPR, TREE_TYPE (dest), dest, len));
+	  return expand_expr (expr, target, mode, EXPAND_NORMAL);
+	}
+
+      /* __memmove_chk special case.  */
+      if (fcode == BUILT_IN_MEMMOVE_CHK)
+        {
+	  unsigned int src_align
+	    = get_pointer_alignment (src, BIGGEST_ALIGNMENT);
+
+	  if (src_align == 0)
+	    return 0;
+
+	  /* If src is categorized for a readonly section we can use
+	     normal __memcpy_chk.  */
+	  if (readonly_data_expr (src))
+	    {
+	      tree const fn = built_in_decls[BUILT_IN_MEMCPY_CHK];
+	      if (!fn)
+		return 0;
+	      return expand_expr (build_function_call_expr (fn, arglist),
+				  target, mode, EXPAND_NORMAL);
+	    }
+        }
+      return 0;
+    }
+}
+
+/* Emit warning if a buffer overflow is detected at compile time.  */
+
+static void
+maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
+{
+  int arg_mask, is_strlen = 0;
+  tree arglist = TREE_OPERAND (exp, 1), a;
+  tree len, size;
+  location_t locus;
+
+  switch (fcode)
+    {
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STPCPY_CHK:
+    /* For __strcat_chk the warning will be emitted only if overflowing
+       by at least strlen (dest) + 1 bytes.  */
+    case BUILT_IN_STRCAT_CHK:
+      arg_mask = 6;
+      is_strlen = 1;
+      break;
+    case BUILT_IN_STRNCPY_CHK:
+      arg_mask = 12;
+      break;
+    case BUILT_IN_SNPRINTF_CHK:
+    case BUILT_IN_VSNPRINTF_CHK:
+      arg_mask = 10;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  len = NULL_TREE;
+  size = NULL_TREE;
+  for (a = arglist; a && arg_mask; a = TREE_CHAIN (a), arg_mask >>= 1)
+    if (arg_mask & 1)
+      {
+	if (len)
+	  size = a;
+	else
+	  len = a;
+      }
+
+  if (!len || !size)
+    return;
+
+  len = TREE_VALUE (len);
+  size = TREE_VALUE (size);
+
+  if (! host_integerp (size, 1) || integer_all_onesp (size))
+    return;
+
+  if (is_strlen)
+    {
+      len = c_strlen (len, 1);
+      if (! len || ! host_integerp (len, 1) || tree_int_cst_lt (len, size))
+	return;
+    }
+  else if (! host_integerp (len, 1) || ! tree_int_cst_lt (size, len))
+    return;
+
+  locus = EXPR_LOCATION (exp);
+  warning ("%Hcall to %D will always overflow destination buffer",
+           &locus, get_callee_fndecl (exp));
+}
+
+/* Emit warning if a buffer overflow is detected at compile time
+   in __sprintf_chk/__vsprintf_chk calls.  */
+
+static void
+maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree dest, size, len, fmt, flag;
+  const char *fmt_str;
+
+  /* Verify the required arguments in the original call.  */
+  if (! arglist)
+    return;
+  dest = TREE_VALUE (arglist);
+  arglist = TREE_CHAIN (arglist);
+  if (! arglist)
+    return;
+  flag = TREE_VALUE (arglist);
+  arglist = TREE_CHAIN (arglist);
+  if (! arglist)
+    return;
+  size = TREE_VALUE (arglist);
+  arglist = TREE_CHAIN (arglist);
+  if (! arglist)
+    return;
+  fmt = TREE_VALUE (arglist);
+  arglist = TREE_CHAIN (arglist);
+
+  if (! host_integerp (size, 1) || integer_all_onesp (size))
+    return;
+
+  /* Check whether the format is a literal string constant.  */
+  fmt_str = c_getstr (fmt);
+  if (fmt_str == NULL)
+    return;
+
+  /* If the format doesn't contain % args or %%, we know its size.  */
+  if (strchr (fmt_str, '%') == 0)
+    len = build_int_cstu (size_type_node, strlen (fmt_str));
+  /* If the format is "%s" and first ... argument is a string literal,
+     we know it too.  */
+  else if (fcode == BUILT_IN_SPRINTF_CHK && strcmp (fmt_str, "%s") == 0)
+    {
+      tree arg;
+
+      if (! arglist)
+	return;
+      arg = TREE_VALUE (arglist);
+      if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
+	return;
+
+      len = c_strlen (arg, 1);
+      if (!len || ! host_integerp (len, 1))
+	return;
+    }
+  else
+    return;
+
+  if (! tree_int_cst_lt (len, size))
+    {
+      location_t locus = EXPR_LOCATION (exp);
+      warning ("%Hcall to %D will always overflow destination buffer",
+	       &locus, get_callee_fndecl (exp));
+    }
+}
+
 /* Expand expression EXP, which is a call to the bcopy builtin.  Return 0
    if we failed the caller should emit a normal call.  */
 
@@ -3982,7 +4248,7 @@ expand_builtin_strncat (tree arglist, rt
       const char *p = c_getstr (src);
 
       /* If the requested length is zero, or the src parameter string
-          length is zero, return the dst parameter.  */
+         length is zero, return the dst parameter.  */
       if (integer_zerop (len) || (p && *p == '\0'))
 	{
 	  /* Evaluate and ignore the src and len parameters in case
@@ -6107,6 +6373,34 @@ expand_builtin (tree exp, rtx target, rt
 	return target;
       break;
 
+      /* __builtin_object_size should be folded after first tree aliasing
+         pass.  If we get up to here, return unknown.  */
+    case BUILT_IN_OBJECT_SIZE:
+      return constm1_rtx;
+
+    case BUILT_IN_MEMCPY_CHK:
+    case BUILT_IN_MEMPCPY_CHK:
+    case BUILT_IN_MEMMOVE_CHK:
+    case BUILT_IN_MEMSET_CHK:
+      target = expand_builtin_memory_chk (exp, target, mode, fcode);
+      if (target)
+	return target;
+      break;
+
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STPCPY_CHK:
+    case BUILT_IN_STRNCPY_CHK:
+    case BUILT_IN_STRCAT_CHK:
+    case BUILT_IN_SNPRINTF_CHK:
+    case BUILT_IN_VSNPRINTF_CHK:
+      maybe_emit_chk_warning (exp, fcode);
+      break;
+
+    case BUILT_IN_SPRINTF_CHK:
+    case BUILT_IN_VSPRINTF_CHK:
+      maybe_emit_sprintf_chk_warning (exp, fcode);
+      break;
+
     default:	/* just do library call, if unknown builtin */
       break;
     }
@@ -6228,6 +6522,253 @@ fold_builtin_constant_p (tree arglist)
   return 0;
 }
 
+
+/* Helper routine for compute_builtin_object_size.  Compute offset of EXPR
+   within VAR.  Return error_mark_node if unknown.  */
+
+static tree
+compute_object_offset (tree expr, tree var)
+{
+  enum tree_code code = PLUS_EXPR;
+  tree base, off, t;
+
+  if (expr == var)
+    return size_zero_node;
+
+  switch (TREE_CODE (expr))
+    {
+    case COMPONENT_REF:
+      base = compute_object_offset (TREE_OPERAND (expr, 0), var);
+      if (base == error_mark_node)
+        return base;
+
+      t = TREE_OPERAND (expr, 1);
+      off = size_binop (PLUS_EXPR, DECL_FIELD_OFFSET (t),
+                        size_int (tree_low_cst (DECL_FIELD_BIT_OFFSET (t), 1)
+                                  / BITS_PER_UNIT));
+      break;
+
+    case REALPART_EXPR:
+    case NOP_EXPR:
+    case CONVERT_EXPR:
+    case VIEW_CONVERT_EXPR:
+    case NON_LVALUE_EXPR:
+      return compute_object_offset (TREE_OPERAND (expr, 0), var);
+
+    case IMAGPART_EXPR:
+      base = compute_object_offset (TREE_OPERAND (expr, 0), var);
+      if (base == error_mark_node)
+	return base;
+
+      off = TYPE_SIZE_UNIT (TREE_TYPE (expr));
+      break;
+
+    case ARRAY_REF:
+      base = compute_object_offset (TREE_OPERAND (expr, 0), var);
+      if (base == error_mark_node)
+        return base;
+
+      t = TREE_OPERAND (expr, 1);
+      if (TREE_CODE (t) == INTEGER_CST && tree_int_cst_sgn (t) < 0)
+        {
+          code = MINUS_EXPR;
+          t = fold (build1 (NEGATE_EXPR, TREE_TYPE (t), t));
+        }
+      t = convert (sizetype, t);
+      off = size_binop (MULT_EXPR, TYPE_SIZE_UNIT (TREE_TYPE (expr)), t);
+      break;
+
+    default:
+      return error_mark_node;
+    }
+
+  return size_binop (code, base, off);
+}
+
+/* Compute __builtin_object_size value.  */
+
+unsigned HOST_WIDE_INT
+compute_builtin_object_size (tree ptr, int subobject_p)
+{
+  tree pt_var;
+
+  if (TREE_CODE (ptr) == ADDR_EXPR)
+    {
+      pt_var = TREE_OPERAND (ptr, 0);
+      if (REFERENCE_CLASS_P (pt_var))
+	pt_var = get_base_address (pt_var);
+
+      if (pt_var && SSA_VAR_P (pt_var)
+	  && TYPE_SIZE_UNIT (TREE_TYPE (pt_var))
+	  && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (pt_var)), 1))
+	{
+	  tree bytes;
+
+	  if (pt_var != TREE_OPERAND (ptr, 0))
+	    {
+	      tree var;
+
+	      if (subobject_p)
+		{
+		  var = TREE_OPERAND (ptr, 0);
+
+		  while (var != pt_var
+			 && TREE_CODE (var) != BIT_FIELD_REF
+			 && TREE_CODE (var) != COMPONENT_REF
+			 && TREE_CODE (var) != ARRAY_REF
+			 && TREE_CODE (var) != ARRAY_RANGE_REF
+			 && TREE_CODE (var) != REALPART_EXPR
+			 && TREE_CODE (var) != IMAGPART_EXPR)
+		    var = TREE_OPERAND (var, 0);
+		  if (var != pt_var && TREE_CODE (var) == ARRAY_REF)
+		    var = TREE_OPERAND (var, 0);
+		  if (! TYPE_SIZE_UNIT (TREE_TYPE (var))
+		      || ! host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (var)), 1)
+		      || tree_int_cst_lt (TYPE_SIZE_UNIT (TREE_TYPE (pt_var)),
+					  TYPE_SIZE_UNIT (TREE_TYPE (var))))
+		    var = pt_var;
+		}
+	      else
+		var = pt_var;
+
+	      bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var);
+	      if (bytes != error_mark_node)
+		bytes = size_binop (MINUS_EXPR,
+				    TYPE_SIZE_UNIT (TREE_TYPE (var)),
+				    bytes);
+	    }
+	  else
+	    bytes = TYPE_SIZE_UNIT (TREE_TYPE (pt_var));
+
+	  if (host_integerp (bytes, 1))
+	    return tree_low_cst (bytes, 1);
+	}
+    }
+  else if (TREE_CODE (ptr) == CALL_EXPR)
+    {
+      tree callee = get_callee_fndecl (ptr);
+      unsigned int arg_mask = 0;
+      tree arglist = TREE_OPERAND (ptr, 1), a;
+      tree bytes = NULL_TREE;
+
+      if (callee
+	  && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL)
+	switch (DECL_FUNCTION_CODE (callee))
+	  {
+	  case BUILT_IN_MALLOC:
+	  case BUILT_IN_ALLOCA:
+	    arg_mask = 1;
+	    break;
+	  /* 
+	  case BUILT_IN_REALLOC:
+	    arg_mask = 2;
+	    break;
+	   */
+	  case BUILT_IN_CALLOC:
+	    arg_mask = 3;
+	    break;
+	  case BUILT_IN_MEMCPY:
+	  case BUILT_IN_MEMMOVE:
+	  case BUILT_IN_MEMSET:
+	  case BUILT_IN_STRCPY:
+	  case BUILT_IN_STRNCPY:
+	  case BUILT_IN_STRCAT:
+	  case BUILT_IN_STRNCAT:
+	  case BUILT_IN_MEMCPY_CHK:
+	  case BUILT_IN_MEMMOVE_CHK:
+	  case BUILT_IN_MEMSET_CHK:
+	  case BUILT_IN_STRCPY_CHK:
+	  case BUILT_IN_STRNCPY_CHK:
+	  case BUILT_IN_STRCAT_CHK:
+	  case BUILT_IN_STRNCAT_CHK:
+	    /* These functions return their first argument.  */
+	    if (arglist)
+	      return compute_builtin_object_size (TREE_VALUE (arglist),
+						  subobject_p);
+	    break;
+	  default:
+	    break;
+	  }
+
+      for (a = arglist; arg_mask && a; arg_mask >>= 1, a = TREE_CHAIN (a))
+	if (arg_mask & 1)
+	  {
+	    tree arg = TREE_VALUE (a);
+
+	    if (TREE_CODE (arg) != INTEGER_CST)
+	      break;
+
+	    if (! bytes)
+	      bytes = fold_convert (sizetype, arg);
+	    else
+	      bytes = size_binop (MULT_EXPR, bytes,
+				  fold_convert (sizetype, arg));
+	  }
+
+      if (! arg_mask && bytes && host_integerp (bytes, 1))
+	return tree_low_cst (bytes, 1);
+
+      return (unsigned HOST_WIDE_INT) -1;
+    }
+  else if (TREE_CODE (ptr) == SSA_NAME && SSA_NAME_PTR_INFO (ptr))
+    {
+      struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
+
+      return subobject_p ? pi->pt_subobject_size : pi->pt_object_size;
+    }
+
+  return (unsigned HOST_WIDE_INT) -1;      
+}
+
+/* Fold a call to __builtin_object_size if possible.  ARGLIST is the
+   argument list of the call.  */
+
+static tree
+fold_builtin_object_size (tree arglist)
+{
+  tree ptr, so_p, ret = 0;
+  int subobject_p;
+
+  if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return 0;
+
+  ptr = TREE_VALUE (arglist);
+  so_p = TREE_VALUE (TREE_CHAIN (arglist));
+  STRIP_NOPS (so_p);
+
+  /* __builtin_object_size doesn't evaluate side-effects in its arguments;
+     if there are any side-effects, it returns (size_t) -1.  */
+  if (TREE_CODE (so_p) != INTEGER_CST
+      || TREE_SIDE_EFFECTS (ptr))
+    return fold_convert (size_type_node, integer_minus_one_node);
+
+  subobject_p = ! integer_zerop (so_p);
+  if (TREE_CODE (ptr) == ADDR_EXPR)
+    ret = build_int_cstu (size_type_node,
+			  compute_builtin_object_size (ptr, subobject_p));
+
+  else if (TREE_CODE (ptr) == SSA_NAME && SSA_NAME_PTR_INFO (ptr))
+    {
+      unsigned HOST_WIDE_INT bytes;
+
+      /* If object size is not known yet, delay folding until
+	 later.  Maybe subsequent passes will help determining
+	 it.  */
+      bytes = compute_builtin_object_size (ptr, subobject_p);
+      if (bytes < (unsigned HOST_WIDE_INT) -1)
+	ret = build_int_cstu (size_type_node, bytes);
+    }
+
+  if (ret)
+    {
+      ret = force_fit_type (ret, -1, false, false);
+      if (TREE_CONSTANT_OVERFLOW (ret))
+	ret = 0;
+    }
+
+  return ret;
+}
+
 /* Fold a call to __builtin_expect, if we expect that a comparison against
    the argument will fold to a constant.  In practice, this means a true
    constant or the address of a non-weak symbol.  ARGLIST is the argument
@@ -8495,6 +9036,46 @@ fold_builtin_1 (tree exp, bool ignore)
     case BUILT_IN_FPUTS_UNLOCKED:
       return fold_builtin_fputs (arglist, ignore, true, NULL_TREE);
 
+    case BUILT_IN_OBJECT_SIZE:
+      return fold_builtin_object_size (arglist);
+    case BUILT_IN_MEMCPY_CHK:
+    case BUILT_IN_MEMPCPY_CHK:
+    case BUILT_IN_MEMMOVE_CHK:
+    case BUILT_IN_MEMSET_CHK:
+      return fold_builtin_memory_chk (exp, NULL_TREE, ignore,
+				      DECL_FUNCTION_CODE (fndecl));
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STPCPY_CHK:
+      return fold_builtin_stxcpy_chk (exp, NULL_TREE, ignore,
+				      DECL_FUNCTION_CODE (fndecl));
+    case BUILT_IN_STRNCPY_CHK:
+      return fold_builtin_strncpy_chk (exp, NULL_TREE);
+    case BUILT_IN_STRCAT_CHK:
+      return fold_builtin_strcat_chk (exp);
+    case BUILT_IN_STRNCAT_CHK:
+      return fold_builtin_strncat_chk (exp);
+    case BUILT_IN_SPRINTF_CHK:
+    case BUILT_IN_VSPRINTF_CHK:
+      return fold_builtin_sprintf_chk (arglist, DECL_FUNCTION_CODE (fndecl));
+    case BUILT_IN_SNPRINTF_CHK:
+    case BUILT_IN_VSNPRINTF_CHK:
+      return fold_builtin_snprintf_chk (arglist, NULL_TREE,
+					DECL_FUNCTION_CODE (fndecl));
+
+    case BUILT_IN_PRINTF:
+    case BUILT_IN_PRINTF_UNLOCKED:
+    case BUILT_IN_VPRINTF:
+    case BUILT_IN_PRINTF_CHK:
+    case BUILT_IN_VPRINTF_CHK:
+      return fold_builtin_printf (exp, DECL_FUNCTION_CODE (fndecl));
+
+    case BUILT_IN_FPRINTF:
+    case BUILT_IN_FPRINTF_UNLOCKED:
+    case BUILT_IN_VFPRINTF:
+    case BUILT_IN_FPRINTF_CHK:
+    case BUILT_IN_VFPRINTF_CHK:
+      return fold_builtin_fprintf (exp, DECL_FUNCTION_CODE (fndecl));
+
     default:
       break;
     }
@@ -9157,14 +9738,535 @@ simplify_builtin_strcspn (tree arglist)
     }
 }
 
-/* Fold a call to the fputs builtin.  IGNORE is true if the value returned
-   by the builtin will be ignored.  UNLOCKED is true is true if this
-   actually a call to fputs_unlocked.  If LEN in non-NULL, it represents
-   the known length of the string.  Return NULL_TREE if no simplification
-   was possible.  */
+/* Fold a call to the __mem{cpy,pcpy,move,set}_chk builtin.
+   IGNORE is true, if return value can be ignored.  FCODE is the BUILT_IN_*
+   code of the builtin.  If MAXLEN is not NULL, it is maximum length
+   passed as third argument.  */
 
 tree
-fold_builtin_fputs (tree arglist, bool ignore, bool unlocked, tree len)
+fold_builtin_memory_chk (tree exp, tree maxlen, bool ignore,
+			 enum built_in_function fcode)
+{
+  tree dest, src, len, size, fn;
+  tree arglist = TREE_OPERAND (exp, 1);
+
+  if (!validate_arglist (arglist,
+			 POINTER_TYPE,
+			 fcode == BUILT_IN_MEMSET_CHK
+			 ? INTEGER_TYPE : POINTER_TYPE,
+			 INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return 0;
+
+  dest = TREE_VALUE (arglist);
+  /* Actually val for __memset_chk, but it doesn't matter.  */
+  src = TREE_VALUE (TREE_CHAIN (arglist));
+  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+  size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist))));
+
+  /* If SRC and DEST are the same (and not volatile), return DEST
+     (resp. DEST+LEN for __mempcpy_chk).  */
+  if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0))
+    {
+      if (fcode != BUILT_IN_MEMPCPY_CHK)
+	return omit_one_operand (TREE_TYPE (exp), dest, len);
+      else
+	{
+	  tree temp = fold_convert (TREE_TYPE (dest), len);
+	  temp = fold (build2 (PLUS_EXPR, TREE_TYPE (dest), dest, temp));
+	  return fold_convert (TREE_TYPE (exp), temp);
+	}
+    }
+
+  if (! host_integerp (size, 1))
+    return 0;
+
+  if (! integer_all_onesp (size))
+    {
+      if (! host_integerp (len, 1))
+	{
+	  /* If LEN is not constant, try MAXLEN too.
+	     For MAXLEN only allow optimizing into non-_ocs function
+	     if SIZE is >= MAXLEN, never convert to __ocs_fail ().  */
+	  if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1))
+	    {
+	      if (fcode == BUILT_IN_MEMPCPY_CHK && ignore)
+		{
+		  /* (void) __mempcpy_chk () can be optimized into
+		     (void) __memcpy_chk ().  */
+		  fn = built_in_decls[BUILT_IN_MEMCPY_CHK];
+		  if (!fn)
+		    return 0;
+
+		  return build_function_call_expr (fn, arglist);
+		}
+	      return 0;
+	    }
+	  len = maxlen;
+	}
+
+      if (tree_int_cst_lt (size, len))
+	return 0;
+    }
+
+  arglist = build_tree_list (NULL_TREE, len);
+  arglist = tree_cons (NULL_TREE, src, arglist);
+  arglist = tree_cons (NULL_TREE, dest, arglist);
+
+  fn = NULL_TREE;
+  /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume
+     mem{cpy,pcpy,move,set} is available.  */
+  switch (fcode)
+    {
+    case BUILT_IN_MEMCPY_CHK:
+      fn = built_in_decls[BUILT_IN_MEMCPY];
+      break;
+    case BUILT_IN_MEMPCPY_CHK:
+      fn = built_in_decls[BUILT_IN_MEMPCPY];
+      break;
+    case BUILT_IN_MEMMOVE_CHK:
+      fn = built_in_decls[BUILT_IN_MEMMOVE];
+      break;
+    case BUILT_IN_MEMSET_CHK:
+      fn = built_in_decls[BUILT_IN_MEMSET];
+      break;
+    default:
+      break;
+    }
+
+  if (!fn)
+    return 0;
+
+  return build_function_call_expr (fn, arglist);
+}
+
+/* Fold a call to the __st[rp]cpy_chk builtin.
+   IGNORE is true, if return value can be ignored.  FCODE is the BUILT_IN_*
+   code of the builtin.  If MAXLEN is not NULL, it is maximum length of
+   strings passed as second argument.  */
+
+tree
+fold_builtin_stxcpy_chk (tree exp, tree maxlen, bool ignore,
+			 enum built_in_function fcode)
+{
+  tree dest, src, size, len, fn;
+  tree arglist = TREE_OPERAND (exp, 1);
+
+  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE,
+			 VOID_TYPE))
+    return 0;
+
+  dest = TREE_VALUE (arglist);
+  src = TREE_VALUE (TREE_CHAIN (arglist));
+  size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+  /* If SRC and DEST are the same (and not volatile), return DEST.  */
+  if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0))
+    return fold_convert (TREE_TYPE (exp), dest);
+ 
+  if (! host_integerp (size, 1))
+    return 0;
+
+  if (! integer_all_onesp (size))
+    {
+      len = c_strlen (src, 1);
+      if (! len || ! host_integerp (len, 1))
+	{
+	  /* If LEN is not constant, try MAXLEN too.
+	     For MAXLEN only allow optimizing into non-_ocs function
+	     if SIZE is >= MAXLEN, never convert to __ocs_fail ().  */
+	  if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1))
+	    {
+	      if (fcode == BUILT_IN_STPCPY_CHK)
+		{
+		  if (! ignore)
+		    return 0;
+
+		  /* If return value of __stpcpy_chk is ignored,
+		     optimize into __strcpy_chk.  */
+		  fn = built_in_decls[BUILT_IN_STRCPY_CHK];
+		  if (!fn)
+		    return 0;
+
+		  return build_function_call_expr (fn, arglist);
+		}
+
+	      if (! len || TREE_SIDE_EFFECTS (len))
+		return 0;
+
+	      /* If c_strlen returned something, but not a constant,
+		 transform __strcpy_chk into __memcpy_chk.  */
+	      fn = built_in_decls[BUILT_IN_MEMCPY_CHK];
+	      if (!fn)
+		return 0;
+
+	      len = size_binop (PLUS_EXPR, len, ssize_int (1));
+	      arglist = build_tree_list (NULL_TREE, size);
+	      arglist = tree_cons (NULL_TREE, len, arglist);
+	      arglist = tree_cons (NULL_TREE, src, arglist);
+	      arglist = tree_cons (NULL_TREE, dest, arglist);
+	      return fold_convert (TREE_TYPE (exp),
+				   build_function_call_expr (fn, arglist));
+	    }
+	  len = maxlen;
+	}
+      
+      if (! tree_int_cst_lt (len, size))
+	return 0;
+    }
+
+  arglist = build_tree_list (NULL_TREE, src);
+  arglist = tree_cons (NULL_TREE, dest, arglist);
+
+  /* If __builtin_st{r,p}cpy_chk is used, assume st{r,p}cpy is available.  */
+  fn = built_in_decls[fcode == BUILT_IN_STPCPY_CHK
+		      ? BUILT_IN_STPCPY : BUILT_IN_STRCPY];
+  if (!fn)
+    return 0;
+
+  return build_function_call_expr (fn, arglist);
+}
+
+/* Fold a call to the __strncpy_chk builtin.
+   If MAXLEN is not NULL, it is maximum length passed as third argument.  */
+
+tree
+fold_builtin_strncpy_chk (tree exp, tree maxlen)
+{
+  tree dest, src, size, len, fn;
+  tree arglist = TREE_OPERAND (exp, 1);
+
+  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE,
+			 INTEGER_TYPE, VOID_TYPE))
+    return 0;
+
+  dest = TREE_VALUE (arglist);
+  src = TREE_VALUE (TREE_CHAIN (arglist));
+  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+  size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist))));
+
+  if (! host_integerp (size, 1))
+    return 0;
+
+  if (! integer_all_onesp (size))
+    {
+      if (! host_integerp (len, 1))
+	{
+	  /* If LEN is not constant, try MAXLEN too.
+	     For MAXLEN only allow optimizing into non-_ocs function
+	     if SIZE is >= MAXLEN, never convert to __ocs_fail ().  */
+	  if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1))
+	    return 0;
+	  len = maxlen;
+	}
+
+      if (tree_int_cst_lt (size, len))
+	return 0;
+    }
+
+  arglist = build_tree_list (NULL_TREE, len);
+  arglist = tree_cons (NULL_TREE, src, arglist);
+  arglist = tree_cons (NULL_TREE, dest, arglist);
+
+  /* If __builtin_strncpy_chk is used, assume strncpy is available.  */
+  fn = built_in_decls[BUILT_IN_STRNCPY];
+  if (!fn)
+    return 0;
+
+  return build_function_call_expr (fn, arglist);
+}
+
+/* Fold a call to the __strcat_chk builtin EXP.  */
+
+static tree
+fold_builtin_strcat_chk (tree exp)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree dest, src, size, fn;
+  const char *p;
+
+  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE,
+			 VOID_TYPE))
+    return 0;
+
+  dest = TREE_VALUE (arglist);
+  src = TREE_VALUE (TREE_CHAIN (arglist));
+  size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+  p = c_getstr (src);
+  /* If the SRC parameter is "", return DEST.  */
+  if (p && *p == '\0')
+    return omit_one_operand (TREE_TYPE (exp), dest, src);
+
+  if (! host_integerp (size, 1) || ! integer_all_onesp (size))
+    return 0;
+
+  arglist = build_tree_list (NULL_TREE, src);
+  arglist = tree_cons (NULL_TREE, dest, arglist);
+
+  /* If __builtin_strcat_chk is used, assume strcat is available.  */
+  fn = built_in_decls[BUILT_IN_STRCAT];
+  if (!fn)
+    return 0;
+
+  return build_function_call_expr (fn, arglist);
+}
+
+/* Fold a call to the __strncat_chk builtin EXP.  */
+
+static tree
+fold_builtin_strncat_chk (tree exp)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree dest, src, size, len, fn;
+  const char *p;
+
+  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE,
+			 INTEGER_TYPE, VOID_TYPE))
+    return 0;
+
+  dest = TREE_VALUE (arglist);
+  src = TREE_VALUE (TREE_CHAIN (arglist));
+  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+  size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist))));
+
+  p = c_getstr (src);
+  /* If the SRC parameter is "" or if LEN is 0, return DEST.  */
+  if (p && *p == '\0')
+    return omit_one_operand (TREE_TYPE (exp), dest, len);
+  else if (integer_zerop (len))
+    return omit_one_operand (TREE_TYPE (exp), dest, src);
+
+  if (! host_integerp (size, 1))
+    return 0;
+
+  if (! integer_all_onesp (size))
+    {
+      tree src_len = c_strlen (src, 1);
+      if (src_len
+	  && host_integerp (src_len, 1)
+	  && host_integerp (len, 1)
+	  && ! tree_int_cst_lt (len, src_len))
+	{
+	  /* If LEN >= strlen (SRC), optimize into __strcat_chk.  */
+	  fn = built_in_decls[BUILT_IN_STRCAT_CHK];
+	  if (!fn)
+	    return 0;
+
+	  arglist = build_tree_list (NULL_TREE, size);
+	  arglist = tree_cons (NULL_TREE, src, arglist);
+	  arglist = tree_cons (NULL_TREE, dest, arglist);
+	  return build_function_call_expr (fn, arglist);
+	}
+      return 0;
+    }
+
+  arglist = build_tree_list (NULL_TREE, len);
+  arglist = tree_cons (NULL_TREE, src, arglist);
+  arglist = tree_cons (NULL_TREE, dest, arglist);
+
+  /* If __builtin_strncat_chk is used, assume strncat is available.  */
+  fn = built_in_decls[BUILT_IN_STRNCAT];
+  if (!fn)
+    return 0;
+
+  return build_function_call_expr (fn, arglist);
+}
+
+/* Fold a call to __{,v}sprintf_chk with argument list ARGLIST.  Return 0 if
+   a normal call should be emitted rather than expanding the function
+   inline.  FCODE is either BUILT_IN_SPRINTF_CHK or BUILT_IN_VSPRINTF_CHK.  */
+
+static tree
+fold_builtin_sprintf_chk (tree arglist, enum built_in_function fcode)
+{
+  tree dest, size, len, fn, fmt, flag;
+  const char *fmt_str;
+
+  /* Verify the required arguments in the original call.  */
+  if (! arglist)
+    return 0;
+  dest = TREE_VALUE (arglist);
+  if (TREE_CODE (TREE_TYPE (dest)) != POINTER_TYPE)
+    return 0;
+  arglist = TREE_CHAIN (arglist);
+  if (! arglist)
+    return 0;
+  flag = TREE_VALUE (arglist);
+  if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE)
+    return 0;
+  arglist = TREE_CHAIN (arglist);
+  if (! arglist)
+    return 0;
+  size = TREE_VALUE (arglist);
+  if (TREE_CODE (TREE_TYPE (size)) != INTEGER_TYPE)
+    return 0;
+  arglist = TREE_CHAIN (arglist);
+  if (! arglist)
+    return 0;
+  fmt = TREE_VALUE (arglist);
+  if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE)
+    return 0;
+  arglist = TREE_CHAIN (arglist);
+
+  if (! host_integerp (size, 1))
+    return 0;
+
+  len = NULL_TREE;
+
+  /* Check whether the format is a literal string constant.  */
+  fmt_str = c_getstr (fmt);
+  if (fmt_str != NULL)
+    {
+      /* If the format doesn't contain % args or %%, we know the size.  */
+      if (strchr (fmt_str, '%') == 0)
+	{
+	  if (fcode != BUILT_IN_SPRINTF_CHK || arglist == NULL_TREE)
+	    len = build_int_cstu (size_type_node, strlen (fmt_str));
+	}
+      /* If the format is "%s" and first ... argument is a string literal,
+         we know the size too.  */
+      else if (fcode == BUILT_IN_SPRINTF_CHK && strcmp (fmt_str, "%s") == 0)
+	{
+	  tree arg;
+
+	  if (arglist && !TREE_CHAIN (arglist))
+	    {
+	      arg = TREE_VALUE (arglist);
+	      if (TREE_CODE (TREE_TYPE (arg)) == POINTER_TYPE)
+		{
+		  len = c_strlen (arg, 1);
+		  if (! len || ! host_integerp (len, 1))
+		    len = NULL_TREE;
+		}
+	    }
+	}
+    }
+
+  if (! integer_all_onesp (size))
+    {
+      if (! len || ! tree_int_cst_lt (len, size))
+	return 0;
+    }
+
+  /* Only convert __{,v}sprintf_chk to {,v}sprintf if flag is 0
+     or if format doesn't contain % chars or is "%s".  */
+  if (! integer_zerop (flag))
+    {
+      if (fmt_str == NULL)
+	return 0;
+      if (strchr (fmt_str, '%') != NULL && strcmp (fmt_str, "%s"))
+	return 0;
+    }
+
+  arglist = tree_cons (NULL_TREE, fmt, arglist);
+  arglist = tree_cons (NULL_TREE, dest, arglist);
+
+  /* If __builtin_{,v}sprintf_chk is used, assume {,v}sprintf is available.  */
+  fn = built_in_decls[fcode == BUILT_IN_VSPRINTF_CHK
+		      ? BUILT_IN_VSPRINTF : BUILT_IN_SPRINTF];
+  if (!fn)
+    return 0;
+
+  return build_function_call_expr (fn, arglist);
+}
+
+/* Fold a call to {,v}snprintf with argument list ARGLIST.  Return 0 if
+   a normal call should be emitted rather than expanding the function
+   inline.  FCODE is either BUILT_IN_SNPRINTF_CHK or
+   BUILT_IN_VSNPRINTF_CHK.  If MAXLEN is not NULL, it is maximum length
+   passed as second argument.  */
+
+tree
+fold_builtin_snprintf_chk (tree arglist, tree maxlen,
+			   enum built_in_function fcode)
+{
+  tree dest, size, len, fn, fmt, flag;
+  const char *fmt_str;
+
+  /* Verify the required arguments in the original call.  */
+  if (! arglist)
+    return 0;
+  dest = TREE_VALUE (arglist);
+  if (TREE_CODE (TREE_TYPE (dest)) != POINTER_TYPE)
+    return 0;
+  arglist = TREE_CHAIN (arglist);
+  if (! arglist)
+    return 0;
+  len = TREE_VALUE (arglist);
+  if (TREE_CODE (TREE_TYPE (len)) != INTEGER_TYPE)
+    return 0;
+  arglist = TREE_CHAIN (arglist);
+  if (! arglist)
+    return 0;
+  flag = TREE_VALUE (arglist);
+  if (TREE_CODE (TREE_TYPE (len)) != INTEGER_TYPE)
+    return 0;
+  arglist = TREE_CHAIN (arglist);
+  if (! arglist)
+    return 0;
+  size = TREE_VALUE (arglist);
+  if (TREE_CODE (TREE_TYPE (size)) != INTEGER_TYPE)
+    return 0;
+  arglist = TREE_CHAIN (arglist);
+  if (! arglist)
+    return 0;
+  fmt = TREE_VALUE (arglist);
+  if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE)
+    return 0;
+  arglist = TREE_CHAIN (arglist);
+
+  if (! host_integerp (size, 1))
+    return 0;
+
+  if (! integer_all_onesp (size))
+    {
+      if (! host_integerp (len, 1))
+	{
+	  /* If LEN is not constant, try MAXLEN too.
+	     For MAXLEN only allow optimizing into non-_ocs function
+	     if SIZE is >= MAXLEN, never convert to __ocs_fail ().  */
+	  if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1))
+	    return 0;
+	  len = maxlen;
+	}
+
+      if (tree_int_cst_lt (size, len))
+	return 0;
+    }
+
+  /* Only convert __{,v}snprintf_chk to {,v}snprintf if flag is 0
+     or if format doesn't contain % chars or is "%s".  */
+  if (! integer_zerop (flag))
+    {
+      fmt_str = c_getstr (fmt);
+      if (fmt_str == NULL)
+	return 0;
+      if (strchr (fmt_str, '%') != NULL && strcmp (fmt_str, "%s"))
+	return 0;
+    }
+
+  arglist = tree_cons (NULL_TREE, fmt, arglist);
+  arglist = tree_cons (NULL_TREE, len, arglist);
+  arglist = tree_cons (NULL_TREE, dest, arglist);
+
+  /* If __builtin_{,v}snprintf_chk is used, assume {,v}snprintf is
+     available.  */
+  fn = built_in_decls[fcode == BUILT_IN_VSNPRINTF_CHK
+		      ? BUILT_IN_VSNPRINTF : BUILT_IN_SNPRINTF];
+  if (!fn)
+    return 0;
+
+  return build_function_call_expr (fn, arglist);
+}
+
+/* Fold a call to the fputs builtin.  IGNORE is true if the value returned
+   by the builtin will be ignored.  UNLOCKED is true is true if this
+   actually a call to fputs_unlocked.  If LEN in non-NULL, it represents
+   the known length of the string.  Return NULL_TREE if no simplification
+   was possible.  */
+
+tree
+fold_builtin_fputs (tree arglist, bool ignore, bool unlocked, tree len)
 {
   tree fn;
   tree fn_fputc = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED]
@@ -9367,3 +10469,268 @@ simplify_builtin_sprintf (tree arglist, 
   else
     return call;
 }
+
+/* Fold a call to the {,v}printf{,_unlocked} and __{,v}printf_chk builtins.
+
+   Return 0 if no simplification was possible, otherwise return the
+   simplified form of the call as a tree.  FCODE is the BUILT_IN_*
+   code of the function to be simplified.  */
+
+static tree
+fold_builtin_printf (tree exp, enum built_in_function fcode)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree fmt, fn = NULL_TREE, fn_putchar, fn_puts, arg, call;
+  const char *fmt_str = NULL;
+
+  /* Verify the required arguments in the original call.  */
+  if (fcode == BUILT_IN_PRINTF_CHK || fcode == BUILT_IN_VPRINTF_CHK)
+    {
+      tree flag;
+
+      if (! arglist)
+	return 0;
+      flag = TREE_VALUE (arglist);
+      if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE
+	  || TREE_SIDE_EFFECTS (flag))
+	return 0;
+      arglist = TREE_CHAIN (arglist);
+    }
+
+  if (! arglist)
+    return 0;
+  fmt = TREE_VALUE (arglist);
+  if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE)
+    return 0;
+  arglist = TREE_CHAIN (arglist);
+
+  /* Check whether the format is a literal string constant.  */
+  fmt_str = c_getstr (fmt);
+  if (fmt_str == NULL)
+    return NULL_TREE;
+
+  if (fcode == BUILT_IN_PRINTF_UNLOCKED)
+    {
+      fn_putchar = implicit_built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED];
+      fn_puts = implicit_built_in_decls[BUILT_IN_PUTS_UNLOCKED];
+    }
+  else
+    {
+      fn_putchar = implicit_built_in_decls[BUILT_IN_PUTCHAR];
+      fn_puts = implicit_built_in_decls[BUILT_IN_PUTS];
+    }
+
+  if (strcmp (fmt_str, "%s") == 0 || strchr (fmt_str, '%') == NULL)
+    {
+      const char *str;
+
+      if (strcmp (fmt_str, "%s") == 0)
+	{
+	  if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK)
+	    return 0;
+
+	  if (! arglist
+	      || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+	      || TREE_CHAIN (arglist))
+	    return 0;
+
+	  str = c_getstr (TREE_VALUE (arglist));
+	  if (str == NULL)
+	    return 0;
+	}
+      else
+	{
+	  /* The format specifier doesn't contain any '%' characters.  */
+	  if (fcode != BUILT_IN_VPRINTF && fcode != BUILT_IN_VPRINTF_CHK
+	      && arglist)
+	    return 0;
+	  str = fmt_str;
+	}
+
+      /* If the string was "", printf does nothing.  */
+      if (str[0] == '\0')
+	return build_int_cst (TREE_TYPE (exp), 0);
+
+      /* If the string has length of 1, call putchar.  */
+      if (str[1] == '\0')
+	{
+          /* Given printf("c"), (where c is any one character,)
+	     convert "c"[0] to an int and pass that to the replacement
+	     function.  */
+	  arg = build_int_cst (NULL_TREE, str[0]);
+	  arglist = build_tree_list (NULL_TREE, arg);
+	  fn = fn_putchar;
+	}
+      else
+	{
+	  /* If the string was "string\n", call puts("string").  */
+	  size_t len = strlen (str);
+	  if (str[len - 1] == '\n')
+	    {
+	      /* Create a NUL-terminated string that's one char shorter
+		 than the original, stripping off the trailing '\n'.  */
+	      char *newstr = alloca (len);
+	      memcpy (newstr, str, len - 1);
+	      newstr[len - 1] = 0;
+
+	      arg = build_string_literal (len, newstr);
+	      arglist = build_tree_list (NULL_TREE, arg);
+	      fn = fn_puts;
+            }
+          else
+            /* We'd like to arrange to call fputs(string,stdout) here,
+               but we need stdout and don't have a way to get it yet.  */
+            return 0;
+        }
+    }
+
+  /* The other optimizations can be done only on the non-va_list variants.  */
+  else if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK)
+    return 0;
+
+  /* If the format specifier was "%s\n", call __builtin_puts(arg).  */
+  else if (strcmp (fmt_str, "%s\n") == 0)
+    {
+      if (! arglist
+          || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+          || TREE_CHAIN (arglist))
+        return 0;
+      fn = fn_puts;
+    }
+
+  /* If the format specifier was "%c", call __builtin_putchar(arg).  */
+  else if (strcmp (fmt_str, "%c") == 0)
+    {
+      if (! arglist
+          || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE
+          || TREE_CHAIN (arglist))
+        return 0;
+      fn = fn_putchar;
+    }
+
+  if (!fn)
+    return 0;
+
+  call = build_function_call_expr (fn, arglist);
+  return fold_convert (TREE_TYPE (exp), call);
+}
+
+/* Fold a call to the {,v}fprintf{,_unlocked} and __{,v}printf_chk builtins.
+
+   Return 0 if no simplification was possible, otherwise return the
+   simplified form of the call as a tree.  FCODE is the BUILT_IN_*
+   code of the function to be simplified.  */
+
+static tree
+fold_builtin_fprintf (tree exp, enum built_in_function fcode)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree fp, fmt, fn = NULL_TREE, fn_fputc, fn_fputs, arg, call;
+  const char *fmt_str = NULL;
+
+  /* Verify the required arguments in the original call.  */
+  if (! arglist)
+    return 0;
+  fp = TREE_VALUE (arglist);
+  if (TREE_CODE (TREE_TYPE (fp)) != POINTER_TYPE)
+    return 0;
+  arglist = TREE_CHAIN (arglist);
+
+  if (fcode == BUILT_IN_FPRINTF_CHK || fcode == BUILT_IN_VFPRINTF_CHK)
+    {
+      tree flag;
+
+      if (! arglist)
+	return 0;
+      flag = TREE_VALUE (arglist);
+      if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE
+	  || TREE_SIDE_EFFECTS (flag))
+	return 0;
+      arglist = TREE_CHAIN (arglist);
+    }
+
+  if (! arglist)
+    return 0;
+  fmt = TREE_VALUE (arglist);
+  if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE)
+    return 0;
+  arglist = TREE_CHAIN (arglist);
+
+  /* Check whether the format is a literal string constant.  */
+  fmt_str = c_getstr (fmt);
+  if (fmt_str == NULL)
+    return NULL_TREE;
+
+  if (fcode == BUILT_IN_FPRINTF_UNLOCKED)
+    {
+      fn_fputc = implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED];
+      fn_fputs = implicit_built_in_decls[BUILT_IN_FPUTS_UNLOCKED];
+    }
+  else
+    {
+      fn_fputc = implicit_built_in_decls[BUILT_IN_FPUTC];
+      fn_fputs = implicit_built_in_decls[BUILT_IN_FPUTS];
+    }
+
+  /* If the format doesn't contain % args or %%, use strcpy.  */
+  if (strchr (fmt_str, '%') == NULL)
+    {
+      if (fcode != BUILT_IN_VFPRINTF && fcode != BUILT_IN_VFPRINTF_CHK
+	  && arglist)
+	return 0;
+
+      /* If the format specifier was "", fprintf does nothing.  */
+      if (fmt_str[0] == '\0')
+	{
+	  /* If FP has side-effects, just wait until gimplification is
+	     done.  */
+	  if (TREE_SIDE_EFFECTS (fp))
+	    return 0;
+
+	  return build_int_cst (TREE_TYPE (exp), 0);
+	}
+
+      /* When "string" doesn't contain %, replace all cases of
+         fprintf (fp, string) with fputs (string, fp).  The fputs
+         builtin will take care of special cases like length == 1.  */
+      arglist = build_tree_list (NULL_TREE, fp);
+      arglist = tree_cons (NULL_TREE, fmt, arglist);
+      fn = fn_fputs;
+    }
+
+  /* The other optimizations can be done only on the non-va_list variants.  */
+  else if (fcode == BUILT_IN_VFPRINTF || fcode == BUILT_IN_VFPRINTF_CHK)
+    return 0;
+
+  /* If the format specifier was "%s", call __builtin_fputs (arg, fp).  */
+  else if (strcmp (fmt_str, "%s") == 0)
+    {
+      if (! arglist
+          || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+          || TREE_CHAIN (arglist))
+        return 0;
+      arg = TREE_VALUE (arglist);
+      arglist = build_tree_list (NULL_TREE, fp);
+      arglist = tree_cons (NULL_TREE, arg, arglist);
+      fn = fn_fputs;
+    }
+
+  /* If the format specifier was "%c", call __builtin_fputc (arg, fp).  */
+  else if (strcmp (fmt_str, "%c") == 0)
+    {
+      if (! arglist
+          || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE
+          || TREE_CHAIN (arglist))
+        return 0;
+      arg = TREE_VALUE (arglist);
+      arglist = build_tree_list (NULL_TREE, fp);
+      arglist = tree_cons (NULL_TREE, arg, arglist);
+      fn = fn_fputc;
+    }
+
+  if (!fn)
+    return 0;
+
+  call = build_function_call_expr (fn, arglist);
+  return fold_convert (TREE_TYPE (exp), call);
+}
--- gcc/builtin-types.def.jj	2004-07-10 09:51:47.000000000 +0200
+++ gcc/builtin-types.def	2004-09-20 18:09:25.179584680 +0200
@@ -32,6 +32,7 @@ Software Foundation, 59 Temple Place - S
    DEF_FUNCTION_TYPE_2 (ENUM, RETURN, ARG1, ARG2)
    DEF_FUNCTION_TYPE_3 (ENUM, RETURN, ARG1, ARG2, ARG3)
    DEF_FUNCTION_TYPE_4 (ENUM, RETURN, ARG1, ARG2, ARG3, ARG4)
+   DEF_FUNCTION_TYPE_5 (ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5)
 
      These macros describe function types.  ENUM is as above.  The
      RETURN type is one of the enumerals already defined.  ARG1, ARG2,
@@ -41,6 +42,7 @@ Software Foundation, 59 Temple Place - S
    DEF_FUNCTION_TYPE_VAR_1 (ENUM, RETURN, ARG1)
    DEF_FUNCTION_TYPE_VAR_2 (ENUM, RETURN, ARG1, ARG2)
    DEF_FUNCTION_TYPE_VAR_3 (ENUM, RETURN, ARG1, ARG2, ARG3)
+   DEF_FUNCTION_TYPE_VAR_4 (ENUM, RETURN, ARG1, ARG2, ARG3, ARG4)
 
      Similar, but for function types that take variable arguments.
      For example:
@@ -239,6 +241,7 @@ DEF_FUNCTION_TYPE_2 (BT_FN_COMPLEX_LONGD
 DEF_FUNCTION_TYPE_2 (BT_FN_VOID_PTR_PTR, BT_VOID, BT_PTR, BT_PTR)
 DEF_FUNCTION_TYPE_2 (BT_FN_INT_CONST_STRING_PTR_CONST_STRING,
 		     BT_INT, BT_CONST_STRING, BT_PTR_CONST_STRING)
+DEF_FUNCTION_TYPE_2 (BT_FN_SIZE_CONST_PTR_INT, BT_SIZE, BT_CONST_PTR, BT_INT)
 
 DEF_FUNCTION_TYPE_3 (BT_FN_STRING_STRING_CONST_STRING_SIZE,
 		     BT_STRING, BT_STRING, BT_CONST_STRING, BT_SIZE)
@@ -283,6 +286,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_LONGDOUB
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_PTR_PTR, BT_VOID, BT_PTR, BT_PTR, BT_PTR)
 DEF_FUNCTION_TYPE_3 (BT_FN_INT_CONST_STRING_PTR_CONST_STRING_PTR_CONST_STRING,
 		     BT_INT, BT_CONST_STRING, BT_PTR_CONST_STRING, BT_PTR_CONST_STRING)
+DEF_FUNCTION_TYPE_3 (BT_FN_INT_INT_CONST_STRING_VALIST_ARG,
+		     BT_INT, BT_INT, BT_CONST_STRING, BT_VALIST_ARG)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
 		     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
@@ -290,6 +295,22 @@ DEF_FUNCTION_TYPE_4 (BT_FN_INT_STRING_SI
 		BT_INT, BT_STRING, BT_SIZE, BT_CONST_STRING, BT_VALIST_ARG)
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_STRING_SIZE_CONST_STRING_CONST_PTR,
 		BT_SIZE, BT_STRING, BT_SIZE, BT_CONST_STRING, BT_CONST_PTR)
+DEF_FUNCTION_TYPE_4 (BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE,
+		     BT_PTR, BT_PTR, BT_CONST_PTR, BT_SIZE, BT_SIZE)
+DEF_FUNCTION_TYPE_4 (BT_FN_PTR_PTR_INT_SIZE_SIZE,
+		     BT_PTR, BT_PTR, BT_INT, BT_SIZE, BT_SIZE)
+DEF_FUNCTION_TYPE_4 (BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE,
+		     BT_STRING, BT_STRING, BT_CONST_STRING, BT_SIZE, BT_SIZE)
+DEF_FUNCTION_TYPE_4 (BT_FN_INT_FILEPTR_INT_CONST_STRING_VALIST_ARG,
+		     BT_INT, BT_FILEPTR, BT_INT, BT_CONST_STRING, BT_VALIST_ARG)
+
+DEF_FUNCTION_TYPE_5 (BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VALIST_ARG,
+		     BT_INT, BT_STRING, BT_INT, BT_SIZE, BT_CONST_STRING,
+		     BT_VALIST_ARG)
+
+DEF_FUNCTION_TYPE_6 (BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VALIST_ARG,
+		     BT_INT, BT_STRING, BT_SIZE, BT_INT, BT_SIZE,
+		     BT_CONST_STRING, BT_VALIST_ARG)
 
 DEF_FUNCTION_TYPE_VAR_0 (BT_FN_VOID_VAR, BT_VOID)
 DEF_FUNCTION_TYPE_VAR_0 (BT_FN_INT_VAR, BT_INT)
@@ -308,11 +329,22 @@ DEF_FUNCTION_TYPE_VAR_2 (BT_FN_INT_STRIN
 			 BT_INT, BT_STRING, BT_CONST_STRING)
 DEF_FUNCTION_TYPE_VAR_2 (BT_FN_INT_CONST_STRING_CONST_STRING_VAR,
 			 BT_INT, BT_CONST_STRING, BT_CONST_STRING)
+DEF_FUNCTION_TYPE_VAR_2 (BT_FN_INT_INT_CONST_STRING_VAR,
+			 BT_INT, BT_INT, BT_CONST_STRING)
 
 DEF_FUNCTION_TYPE_VAR_3 (BT_FN_INT_STRING_SIZE_CONST_STRING_VAR,
 			 BT_INT, BT_STRING, BT_SIZE, BT_CONST_STRING)
 DEF_FUNCTION_TYPE_VAR_3 (BT_FN_SSIZE_STRING_SIZE_CONST_STRING_VAR,
 			 BT_SSIZE, BT_STRING, BT_SIZE, BT_CONST_STRING)
+DEF_FUNCTION_TYPE_VAR_3 (BT_FN_INT_FILEPTR_INT_CONST_STRING_VAR,
+			 BT_INT, BT_FILEPTR, BT_INT, BT_CONST_STRING)
+
+DEF_FUNCTION_TYPE_VAR_4 (BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VAR,
+			 BT_INT, BT_STRING, BT_INT, BT_SIZE, BT_CONST_STRING)
+
+DEF_FUNCTION_TYPE_VAR_5 (BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VAR,
+			 BT_INT, BT_STRING, BT_SIZE, BT_INT, BT_SIZE,
+			 BT_CONST_STRING)
 
 DEF_POINTER_TYPE (BT_PTR_FN_VOID_VAR, BT_FN_VOID_VAR)
 DEF_FUNCTION_TYPE_3 (BT_FN_PTR_PTR_FN_VOID_VAR_PTR_SIZE,
--- gcc/Makefile.in.jj	2004-09-18 13:49:33.000000000 +0200
+++ gcc/Makefile.in	2004-09-20 18:09:25.210579193 +0200
@@ -1859,7 +1859,7 @@ builtins.o : builtins.c $(CONFIG_H) $(SY
    $(TREE_GIMPLE_H) $(FLAGS_H) $(TARGET_H) function.h $(REGS_H) $(EXPR_H) $(OPTABS_H) \
    insn-config.h $(RECOG_H) output.h typeclass.h hard-reg-set.h toplev.h hard-reg-set.h \
    except.h $(TM_P_H) $(PREDICT_H) libfuncs.h real.h langhooks.h basic-block.h \
-   tree-mudflap.h
+   tree-mudflap.h $(TREE_FLOW_H)
 calls.o : calls.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) $(FLAGS_H) \
    $(EXPR_H) $(OPTABS_H) langhooks.h $(TARGET_H) \
    libfuncs.h $(REGS_H) toplev.h output.h function.h $(TIMEVAR_H) $(TM_P_H) $(CGRAPH_H) except.h
--- gcc/doc/extend.texi.jj	2004-09-18 13:49:38.000000000 +0200
+++ gcc/doc/extend.texi	2004-09-20 18:09:25.221577247 +0200
@@ -70,6 +70,8 @@ extensions, accepted by GCC in C89 mode 
 * Return Address::      Getting the return or frame address of a function.
 * Vector Extensions::   Using vector instructions through built-in functions.
 * Offsetof::            Special syntax for implementing @code{offsetof}.
+* Object Size Checking:: Built-in functions for limited buffer overflow
+			 checking.
 * Other Builtins::      Other built-in functions.
 * Target Builtins::     Built-in functions specific to particular targets.
 * Target Format Checks:: Format checks specific to particular targets.
@@ -4412,6 +4414,134 @@ is a suitable definition of the @code{of
 may be dependent.  In either case, @var{member} may consist of a single
 identifier, or a sequence of member accesses and array references.
 
+@node Object Size Checking
+@section Object Size Checking Builtins
+@findex __builtin_object_size
+@findex __builtin___memcpy_chk
+@findex __builtin___mempcpy_chk
+@findex __builtin___memmove_chk
+@findex __builtin___memset_chk
+@findex __builtin___strcpy_chk
+@findex __builtin___stpcpy_chk
+@findex __builtin___strncpy_chk
+@findex __builtin___strcat_chk
+@findex __builtin___strncat_chk
+@findex __builtin___sprintf_chk
+@findex __builtin___snprintf_chk
+@findex __builtin___vsprintf_chk
+@findex __builtin___vsnprintf_chk
+@findex __builtin___printf_chk
+@findex __builtin___vprintf_chk
+@findex __builtin___fprintf_chk
+@findex __builtin___vfprintf_chk
+
+GCC implements a limited buffer overflow protection mechanism
+that can prevent some buffer overflow attacks.
+
+@deftypefn {Built-in Function} {size_t} __builtin_object_size (void * @var{ptr}, int @var{sublevel_p})
+is a built-in construct that returns a constant number of bytes from
+@var{ptr} to the end of the object @var{ptr} pointer points to
+(if known at compile time).  @code{__builtin_object_size} never evaluates
+its arguments for side-effects, if there are any side-effects in them, it
+returns @code{(size_t) -1}.  If there are multiple objects @var{ptr} can
+point to and all of them are known at compile time, the returned number
+is the maximum of remaining byte counts in those objects.  If it is not
+possible to determine which objects @var{ptr} points to at compile time,
+@code{__builtin_object_size} should return @code{(size_t) -1}.
+
+@var{sublevel_p} can be either constant 0 or 1.  If it is 0, objects are
+whole variables, if it is 1, a closest surrounding subobject is considered
+the object a pointer points to.
+
+@smallexample
+struct V @{ char buf1[10]; int b; char buf2[10]; @} var;
+char *p = &var.buf1[1], *q = &var.b;
+
+/* Here the object p points to is var.  */
+assert (__builtin_object_size (p, 0) == sizeof (var) - 1);
+/* The subobject p points to is var.buf1.  */
+assert (__builtin_object_size (p, 1) == sizeof (var.buf1) - 1);
+/* The object q points to is var.  */
+assert (__builtin_object_size (q, 0)
+	== (char *) (&var + 1) - (char *) &var.b);
+/* The subobject q points to is var.b.  */
+assert (__builtin_object_size (q, 1) == sizeof (var.b));
+@end smallexample
+@end deftypefn
+
+There are built-in functions added for many common string operation
+functions, e.g. for @code{memcpy} @code{__builtin___memcpy_chk}
+built-in is provided.  This built-in has an additional last argument,
+which is the number of bytes remaining in object the @var{dest}
+argument points to or @code{(size_t) -1} if the size is not known.
+
+The built-in functions are optimized into the normal string functions
+like @code{memcpy} if the last argument is @code{(size_t) -1} or if
+it is known at compile time that the destination object will not
+be overflown.  If the compiler can determine at compile time the
+object will be always overflown, it issues a warning.
+
+The intended use can be e.g.
+
+@smallexample
+#undef memcpy
+#define bos(dest) __builtin_object_size (dest, 0)
+#define memcpy(dest, src, n) \
+  __builtin___memcpy_chk (dest, src, n, bos (dest))
+
+char *volatile p;
+char buf[10];
+/* It is unknown what object p points to, so this is optimized
+   into plain memcpy - no checking is possible.  */
+memcpy (p, "abcde", n);
+/* Destination is known and length too.  It is known at compile
+   time there will be no overflow.  */
+memcpy (&buf[5], "abcde", 5);
+/* Destination is known, but the length is not known at compile time.
+   This will result in __memcpy_chk call that can check for overflow
+   at runtime.  */
+memcpy (&buf[5], "abcde", n);
+/* Destination is known and it is known at compile time there will
+   be overflow.  There will be a warning and __memcpy_chk call that
+   will abort the program at runtime.  */
+memcpy (&buf[6], "abcde", 5);
+@end smallexample
+
+Such built-in functions are provided for @code{memcpy}, @code{mempcpy},
+@code{memmove}, @code{memset}, @code{strcpy}, @code{stpcpy}, @code{strncpy},
+@code{strcat} and @code{strncat}.
+
+There are also checking built-in functions for formatted output functions.
+@smallexample
+int __builtin___sprintf_chk (char *s, int flag, size_t os, const char *fmt, ...);
+int __builtin___snprintf_chk (char *s, size_t maxlen, int flag, size_t os,
+			      const char *fmt, ...);
+int __builtin___vsprintf_chk (char *s, int flag, size_t os, const char *fmt,
+			      va_list ap);
+int __builtin___vsnprintf_chk (char *s, size_t maxlen, int flag, size_t os,
+			       const char *fmt, va_list ap);
+@end smallexample
+
+The added @var{flag} argument is passed unchanged to @code{__sprintf_chk}
+etc. functions and can contain implementation specific flags on what
+additional security measures the checking function might take, such as
+handling @code{%n} differently.
+
+The @var{os} argument is the object size @var{s} points to, like in the
+other built-in functions.  There is a small difference in the behaviour
+though, if @var{os} is @code{(size_t) -1}, the built-in functions are
+optimized into the non-checking functions only if @var{flag} is 0, otherwise
+the checking function is called with @var{os} argument set to
+@code{(size_t) -1}.
+
+In addition to this, there are checking built-in functions
+@code{__builtin___printf_chk}, @code{__builtin___vprintf_chk},
+@code{__builtin___fprintf_chk} and @code{__builtin___vfprintf_chk}.
+These have just one additional argument, @var{flag}, right before
+format string @var{fmt}.  If the compiler is able to optimize them to
+@code{fputc} etc. functions, it will, otherwise the checking function
+should be called and the @var{flag} argument passed to it.
+
 @node Other Builtins
 @section Other built-in functions provided by GCC
 @cindex built-in functions
--- gcc/builtins.def.jj	2004-09-11 20:34:50.000000000 +0200
+++ gcc/builtins.def	2004-09-20 18:09:25.196581671 +0200
@@ -624,6 +624,26 @@ DEF_GCC_BUILTIN        (BUILT_IN_INIT_TR
 DEF_GCC_BUILTIN        (BUILT_IN_ADJUST_TRAMPOLINE, "adjust_trampoline", BT_FN_PTR_PTR, ATTR_CONST_NOTHROW_LIST)
 DEF_GCC_BUILTIN        (BUILT_IN_NONLOCAL_GOTO, "nonlocal_goto", BT_FN_PTR_PTR, ATTR_NORETURN_NOTHROW_LIST)
 
+/* Object size checking builtins.  */
+DEF_GCC_BUILTIN	       (BUILT_IN_OBJECT_SIZE, "object_size", BT_FN_SIZE_CONST_PTR_INT, ATTR_PURE_NOTHROW_LIST)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMCPY_CHK, "__memcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL_1_2)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMMOVE_CHK, "__memmove_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL_1_2)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMPCPY_CHK, "__mempcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL_1_2)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMSET_CHK, "__memset_chk", BT_FN_PTR_PTR_INT_SIZE_SIZE, ATTR_NOTHROW_NONNULL_1)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_STPCPY_CHK, "__stpcpy_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL_1_2)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRCAT_CHK, "__strcat_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL_1_2)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRCPY_CHK, "__strcpy_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL_1_2)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRNCAT_CHK, "__strncat_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_NOTHROW_NONNULL_1_2)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRNCPY_CHK, "__strncpy_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_NOTHROW_NONNULL_1_2)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_SNPRINTF_CHK, "__snprintf_chk", BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_5_6)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_SPRINTF_CHK, "__sprintf_chk", BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_4_5)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_VSNPRINTF_CHK, "__vsnprintf_chk", BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_5_0)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_VSPRINTF_CHK, "__vsprintf_chk", BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_4_0)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_FPRINTF_CHK, "__fprintf_chk", BT_FN_INT_FILEPTR_INT_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_3_4)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_PRINTF_CHK, "__printf_chk", BT_FN_INT_INT_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_2_3)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_VFPRINTF_CHK, "__vfprintf_chk", BT_FN_INT_FILEPTR_INT_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_3_0)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_VPRINTF_CHK, "__vprintf_chk", BT_FN_INT_INT_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_2_0)
+
 /* Profiling hooks.  */
 DEF_GCC_BUILTIN (BUILT_IN_PROFILE_FUNC_ENTER, "profile_func_enter",
 		 BT_FN_VOID, ATTR_NULL)
--- gcc/tree-flow.h.jj	2004-09-20 17:53:05.893692996 +0200
+++ gcc/tree-flow.h	2004-09-20 18:09:25.190582733 +0200
@@ -74,6 +74,13 @@ struct ptr_info_def GTY(())
      pointer will be represented by this memory tag, instead of the type
      tag computed by TBAA.  */
   tree name_mem_tag;
+
+  /* Upper bound for number of bytes till the end of the object.  */
+  unsigned HOST_WIDE_INT pt_object_size;
+
+  /* Upper bound for number of bytes till the end of the subobject
+     (innermost array or field with address taken).  */
+  unsigned HOST_WIDE_INT pt_subobject_size;
 };
 
 
--- gcc/c-common.c.jj	2004-09-18 13:49:33.000000000 +0200
+++ gcc/c-common.c	2004-09-20 18:09:25.202580609 +0200
@@ -2864,10 +2864,16 @@ c_common_nodes_and_builtins (void)
 #define DEF_FUNCTION_TYPE_2(NAME, RETURN, ARG1, ARG2) NAME,
 #define DEF_FUNCTION_TYPE_3(NAME, RETURN, ARG1, ARG2, ARG3) NAME,
 #define DEF_FUNCTION_TYPE_4(NAME, RETURN, ARG1, ARG2, ARG3, ARG4) NAME,
+#define DEF_FUNCTION_TYPE_5(NAME, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5) NAME,
+#define DEF_FUNCTION_TYPE_6(NAME, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6) \
+  NAME,
 #define DEF_FUNCTION_TYPE_VAR_0(NAME, RETURN) NAME,
 #define DEF_FUNCTION_TYPE_VAR_1(NAME, RETURN, ARG1) NAME,
 #define DEF_FUNCTION_TYPE_VAR_2(NAME, RETURN, ARG1, ARG2) NAME,
 #define DEF_FUNCTION_TYPE_VAR_3(NAME, RETURN, ARG1, ARG2, ARG3) NAME,
+#define DEF_FUNCTION_TYPE_VAR_4(NAME, RETURN, ARG1, ARG2, ARG3, ARG4) NAME,
+#define DEF_FUNCTION_TYPE_VAR_5(NAME, RETURN, ARG1, ARG2, ARG3, ARG4, ARG6) \
+  NAME,
 #define DEF_POINTER_TYPE(NAME, TYPE) NAME,
 #include "builtin-types.def"
 #undef DEF_PRIMITIVE_TYPE
@@ -2876,10 +2882,14 @@ c_common_nodes_and_builtins (void)
 #undef DEF_FUNCTION_TYPE_2
 #undef DEF_FUNCTION_TYPE_3
 #undef DEF_FUNCTION_TYPE_4
+#undef DEF_FUNCTION_TYPE_5
+#undef DEF_FUNCTION_TYPE_6
 #undef DEF_FUNCTION_TYPE_VAR_0
 #undef DEF_FUNCTION_TYPE_VAR_1
 #undef DEF_FUNCTION_TYPE_VAR_2
 #undef DEF_FUNCTION_TYPE_VAR_3
+#undef DEF_FUNCTION_TYPE_VAR_4
+#undef DEF_FUNCTION_TYPE_VAR_5
 #undef DEF_POINTER_TYPE
     BT_LAST
   };
@@ -3126,6 +3136,42 @@ c_common_nodes_and_builtins (void)
 			      tree_cons (NULL_TREE,			\
 					 builtin_types[(int) ARG4],	\
 					 void_list_node)))));
+#define DEF_FUNCTION_TYPE_5(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5)	\
+  builtin_types[(int) ENUM]						\
+    = build_function_type						\
+      (builtin_types[(int) RETURN],					\
+       tree_cons (NULL_TREE,						\
+		  builtin_types[(int) ARG1],				\
+		  tree_cons (NULL_TREE,					\
+			     builtin_types[(int) ARG2],			\
+			     tree_cons					\
+			     (NULL_TREE,				\
+			      builtin_types[(int) ARG3],		\
+			      tree_cons (NULL_TREE,			\
+					 builtin_types[(int) ARG4],	\
+					 tree_cons (NULL_TREE,		\
+					      builtin_types[(int) ARG5],\
+					      void_list_node))))));
+#define DEF_FUNCTION_TYPE_6(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, \
+			    ARG6)					\
+  builtin_types[(int) ENUM]						\
+    = build_function_type						\
+      (builtin_types[(int) RETURN],					\
+       tree_cons (NULL_TREE,						\
+		  builtin_types[(int) ARG1],				\
+		  tree_cons (NULL_TREE,					\
+			     builtin_types[(int) ARG2],			\
+			     tree_cons					\
+			     (NULL_TREE,				\
+			      builtin_types[(int) ARG3],		\
+			      tree_cons					\
+			      (NULL_TREE,				\
+			       builtin_types[(int) ARG4],		\
+			       tree_cons (NULL_TREE,			\
+					 builtin_types[(int) ARG5],	\
+					 tree_cons (NULL_TREE,		\
+					      builtin_types[(int) ARG6],\
+					      void_list_node)))))));
 #define DEF_FUNCTION_TYPE_VAR_0(ENUM, RETURN)				\
   builtin_types[(int) ENUM]						\
     = build_function_type (builtin_types[(int) RETURN], NULL_TREE);
@@ -3158,6 +3204,38 @@ c_common_nodes_and_builtins (void)
 					builtin_types[(int) ARG3],	\
 					NULL_TREE))));
 
+#define DEF_FUNCTION_TYPE_VAR_4(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4)	\
+   builtin_types[(int) ENUM]						\
+    = build_function_type						\
+      (builtin_types[(int) RETURN],					\
+       tree_cons (NULL_TREE,						\
+		  builtin_types[(int) ARG1],				\
+		  tree_cons (NULL_TREE,					\
+			     builtin_types[(int) ARG2],			\
+			     tree_cons (NULL_TREE,			\
+					builtin_types[(int) ARG3],	\
+					tree_cons (NULL_TREE,		\
+					      builtin_types[(int) ARG4],\
+					      NULL_TREE)))));
+
+#define DEF_FUNCTION_TYPE_VAR_5(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4,	\
+				ARG5)					\
+   builtin_types[(int) ENUM]						\
+    = build_function_type						\
+      (builtin_types[(int) RETURN],					\
+       tree_cons (NULL_TREE,						\
+		  builtin_types[(int) ARG1],				\
+		  tree_cons (NULL_TREE,					\
+			     builtin_types[(int) ARG2],			\
+			     tree_cons					\
+			     (NULL_TREE,				\
+			      builtin_types[(int) ARG3],		\
+			      tree_cons (NULL_TREE,			\
+					builtin_types[(int) ARG4],	\
+					tree_cons (NULL_TREE,		\
+					      builtin_types[(int) ARG5],\
+					      NULL_TREE))))));
+
 #define DEF_POINTER_TYPE(ENUM, TYPE)			\
   builtin_types[(int) ENUM]				\
     = build_pointer_type (builtin_types[(int) TYPE]);
@@ -3167,10 +3245,14 @@ c_common_nodes_and_builtins (void)
 #undef DEF_FUNCTION_TYPE_2
 #undef DEF_FUNCTION_TYPE_3
 #undef DEF_FUNCTION_TYPE_4
+#undef DEF_FUNCTION_TYPE_5
+#undef DEF_FUNCTION_TYPE_6
 #undef DEF_FUNCTION_TYPE_VAR_0
 #undef DEF_FUNCTION_TYPE_VAR_1
 #undef DEF_FUNCTION_TYPE_VAR_2
 #undef DEF_FUNCTION_TYPE_VAR_3
+#undef DEF_FUNCTION_TYPE_VAR_4
+#undef DEF_FUNCTION_TYPE_VAR_5
 #undef DEF_POINTER_TYPE
 
   c_init_attributes ();
--- gcc/tree-ssa-ccp.c.jj	2004-09-20 18:04:32.250418533 +0200
+++ gcc/tree-ssa-ccp.c	2004-09-20 18:11:33.748865678 +0200
@@ -1776,24 +1776,49 @@ fold_stmt_r (tree *expr_p, int *walk_sub
 }
 
 
-/* Return the string length of ARG in LENGTH.  If ARG is an SSA name variable,
-   follow its use-def chains.  If LENGTH is not NULL and its value is not
-   equal to the length we determine, or if we are unable to determine the
-   length, return false.  VISITED is a bitmap of visited variables.  */
+/* Return the string length, maximum string length or maximum value of
+   ARG in LENGTH.
+   If ARG is an SSA name variable, follow its use-def chains.  If LENGTH
+   is not NULL and, for TYPE == 0, its value is not equal to the length
+   we determine or if we are unable to determine the length or value,
+   return false.  VISITED is a bitmap of visited variables.
+   TYPE is 0 if string length should be returned, 1 for maximum string
+   length and 2 for maximum value ARG can have.  */
 
 static bool
-get_strlen (tree arg, tree *length, bitmap visited)
+get_maxval_strlen (tree arg, tree *length, bitmap visited, int type)
 {
   tree var, def_stmt, val;
   
   if (TREE_CODE (arg) != SSA_NAME)
     {
-      val = c_strlen (arg, 1);
+      if (type == 2)
+	{
+	  val = arg;
+	  if (TREE_CODE (val) != INTEGER_CST
+	      || tree_int_cst_sgn (val) < 0)
+	    return false;
+	}
+      else
+        val = c_strlen (arg, 1);
       if (!val)
 	return false;
 
-      if (*length && simple_cst_equal (val, *length) != 1)
-	return false;
+      if (*length)
+	{
+	  if (type > 0)
+	    {
+	      if (TREE_CODE (*length) != INTEGER_CST
+		  || TREE_CODE (val) != INTEGER_CST)
+		return false;
+
+	      if (tree_int_cst_lt (*length, val))
+		*length = val;
+	      return true;
+	    }
+	  else if (simple_cst_equal (val, *length) != 1)
+	    return false;
+	}
 
       *length = val;
       return true;
@@ -1811,28 +1836,14 @@ get_strlen (tree arg, tree *length, bitm
     {
       case MODIFY_EXPR:
 	{
-	  tree len, rhs;
-	  
+	  tree rhs;
+
 	  /* The RHS of the statement defining VAR must either have a
 	     constant length or come from another SSA_NAME with a constant
 	     length.  */
 	  rhs = TREE_OPERAND (def_stmt, 1);
 	  STRIP_NOPS (rhs);
-	  if (TREE_CODE (rhs) == SSA_NAME)
-	    return get_strlen (rhs, length, visited);
-
-	  /* See if the RHS is a constant length.  */
-	  len = c_strlen (rhs, 1);
-	  if (len)
-	    {
-	      if (*length && simple_cst_equal (len, *length) != 1)
-		return false;
-
-	      *length = len;
-	      return true;
-	    }
-
-	  break;
+	  return get_maxval_strlen (rhs, length, visited, type);
 	}
 
       case PHI_NODE:
@@ -1854,7 +1865,7 @@ get_strlen (tree arg, tree *length, bitm
 	      if (arg == PHI_RESULT (def_stmt))
 		continue;
 
-	      if (!get_strlen (arg, length, visited))
+	      if (!get_maxval_strlen (arg, length, visited, type))
 		return false;
 	    }
 
@@ -1876,9 +1887,9 @@ get_strlen (tree arg, tree *length, bitm
 static tree
 ccp_fold_builtin (tree stmt, tree fn)
 {
-  tree result, strlen_val[2];
+  tree result, val[3];
   tree callee, arglist, a;
-  int strlen_arg, i;
+  int arg_mask, i, type;
   bitmap visited;
   bool ignore;
 
@@ -1888,11 +1899,11 @@ ccp_fold_builtin (tree stmt, tree fn)
      result directly.  */
   result = fold_builtin (fn, ignore);
   if (result)
-  {
-    if (ignore)
-      STRIP_NOPS (result);
-    return result;
-  }
+    {
+      if (ignore)
+	STRIP_NOPS (result);
+      return result;
+    }
 
   /* Ignore MD builtins.  */
   callee = get_callee_fndecl (fn);
@@ -1911,11 +1922,31 @@ ccp_fold_builtin (tree stmt, tree fn)
     case BUILT_IN_STRLEN:
     case BUILT_IN_FPUTS:
     case BUILT_IN_FPUTS_UNLOCKED:
-      strlen_arg = 1;
+      arg_mask = 1;
+      type = 0;
       break;
     case BUILT_IN_STRCPY:
     case BUILT_IN_STRNCPY:
-      strlen_arg = 2;
+      arg_mask = 2;
+      type = 0;
+      break;
+    case BUILT_IN_MEMCPY_CHK:
+    case BUILT_IN_MEMPCPY_CHK:
+    case BUILT_IN_MEMMOVE_CHK:
+    case BUILT_IN_MEMSET_CHK:
+    case BUILT_IN_STRNCPY_CHK:
+      arg_mask = 4;
+      type = 2;
+      break;
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STPCPY_CHK:
+      arg_mask = 2;
+      type = 1;
+      break;
+    case BUILT_IN_SNPRINTF_CHK:
+    case BUILT_IN_VSNPRINTF_CHK:
+      arg_mask = 2;
+      type = 2;
       break;
     default:
       return NULL_TREE;
@@ -1924,15 +1955,15 @@ ccp_fold_builtin (tree stmt, tree fn)
   /* Try to use the dataflow information gathered by the CCP process.  */
   visited = BITMAP_XMALLOC ();
 
-  memset (strlen_val, 0, sizeof (strlen_val));
+  memset (val, 0, sizeof (val));
   for (i = 0, a = arglist;
-       strlen_arg;
-       i++, strlen_arg >>= 1, a = TREE_CHAIN (a))
-    if (strlen_arg & 1)
+       arg_mask;
+       i++, arg_mask >>= 1, a = TREE_CHAIN (a))
+    if (arg_mask & 1)
       {
 	bitmap_clear (visited);
-	if (!get_strlen (TREE_VALUE (a), &strlen_val[i], visited))
-	  strlen_val[i] = NULL_TREE;
+	if (!get_maxval_strlen (TREE_VALUE (a), &val[i], visited, type))
+	  val[i] = NULL_TREE;
       }
 
   BITMAP_XFREE (visited);
@@ -1941,9 +1972,9 @@ ccp_fold_builtin (tree stmt, tree fn)
   switch (DECL_FUNCTION_CODE (callee))
     {
     case BUILT_IN_STRLEN:
-      if (strlen_val[0])
+      if (val[0])
 	{
-	  tree new = fold_convert (TREE_TYPE (fn), strlen_val[0]);
+	  tree new = fold_convert (TREE_TYPE (fn), val[0]);
 
 	  /* If the result is not a valid gimple value, or not a cast
 	     of a valid gimple value, then we can not use the result.  */
@@ -1955,25 +1986,53 @@ ccp_fold_builtin (tree stmt, tree fn)
       break;
 
     case BUILT_IN_STRCPY:
-      if (strlen_val[1] && is_gimple_val (strlen_val[1]))
-        result = fold_builtin_strcpy (fn, strlen_val[1]);
+      if (val[1] && is_gimple_val (val[1]))
+        result = fold_builtin_strcpy (fn, val[1]);
       break;
 
     case BUILT_IN_STRNCPY:
-      if (strlen_val[1] && is_gimple_val (strlen_val[1]))
-	result = fold_builtin_strncpy (fn, strlen_val[1]);
+      if (val[1] && is_gimple_val (val[1]))
+	result = fold_builtin_strncpy (fn, val[1]);
       break;
 
     case BUILT_IN_FPUTS:
       result = fold_builtin_fputs (arglist,
 				   TREE_CODE (stmt) != MODIFY_EXPR, 0,
-				   strlen_val[0]);
+				   val[0]);
       break;
 
     case BUILT_IN_FPUTS_UNLOCKED:
       result = fold_builtin_fputs (arglist,
 				   TREE_CODE (stmt) != MODIFY_EXPR, 1,
-				   strlen_val[0]);
+				   val[0]);
+      break;
+
+    case BUILT_IN_MEMCPY_CHK:
+    case BUILT_IN_MEMPCPY_CHK:
+    case BUILT_IN_MEMMOVE_CHK:
+    case BUILT_IN_MEMSET_CHK:
+      if (val[2] && is_gimple_val (val[2]))
+	result = fold_builtin_memory_chk (fn, val[2], ignore,
+					  DECL_FUNCTION_CODE (callee));
+      break;
+
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STPCPY_CHK:
+      if (val[1] && is_gimple_val (val[1]))
+	result = fold_builtin_stxcpy_chk (fn, val[1], ignore,
+					  DECL_FUNCTION_CODE (callee));
+      break;
+
+    case BUILT_IN_STRNCPY_CHK:
+      if (val[2] && is_gimple_val (val[2]))
+	result = fold_builtin_strncpy_chk (fn, val[2]);
+      break;
+
+    case BUILT_IN_SNPRINTF_CHK:
+    case BUILT_IN_VSNPRINTF_CHK:
+      if (val[1] && is_gimple_val (val[1]))
+	result = fold_builtin_snprintf_chk (arglist, val[1],
+					    DECL_FUNCTION_CODE (callee));
       break;
 
     default:
@@ -2070,7 +2129,6 @@ fold_stmt (tree *stmt_p)
 
   return changed;
 }
-
 
 /* Convert EXPR into a GIMPLE value suitable for substitution on the
    RHS of an assignment.  Insert the necessary statements before
@@ -2110,17 +2168,25 @@ execute_fold_all_builtins (void)
   FOR_EACH_BB (bb)
     {
       block_stmt_iterator i;
-      for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i))
+      for (i = bsi_start (bb); !bsi_end_p (i); )
 	{
 	  tree *stmtp = bsi_stmt_ptr (i);
 	  tree call = get_rhs (*stmtp);
 	  tree callee, result;
+	  enum built_in_function fcode;
 
 	  if (!call || TREE_CODE (call) != CALL_EXPR)
-	    continue;
+	    {
+	      bsi_next (&i);
+	      continue;
+	    }
 	  callee = get_callee_fndecl (call);
 	  if (!callee || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL)
-	    continue;
+	    {
+	      bsi_next (&i);
+	      continue;
+	    }
+	  fcode = DECL_FUNCTION_CODE (callee);
 
 	  result = ccp_fold_builtin (*stmtp, call);
 	  if (!result)
@@ -2133,7 +2199,15 @@ execute_fold_all_builtins (void)
 		result = integer_zero_node;
 		break;
 
+	      case BUILT_IN_OBJECT_SIZE:
+		/* Resolve __builtin_object_size.  If it hasn't been
+		   folded to some size by now, it's fairly certain
+		   that the value simply will not be known.  */
+		result = fold_convert (size_type_node, integer_minus_one_node);
+		break;
+
 	      default:
+		bsi_next (&i);
 		continue;
 	      }
 
@@ -2157,6 +2231,20 @@ execute_fold_all_builtins (void)
 	      print_generic_stmt (dump_file, *stmtp, dump_flags);
 	      fprintf (dump_file, "\n");
 	    }
+
+	  /* Retry the same statement if it changed into another
+	     builtin, there might be new opportunities now.  */
+	  call = get_rhs (*stmtp);
+	  if (!call || TREE_CODE (call) != CALL_EXPR)
+	    {
+	      bsi_next (&i);
+	      continue;
+	    }
+	  callee = get_callee_fndecl (call);
+	  if (!callee
+	      || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL
+	      || DECL_FUNCTION_CODE (callee) == fcode)
+	    bsi_next (&i);
 	}
     }
 }
--- gcc/testsuite/gcc.c-torture/execute/builtins/strcat-chk.c.jj	2004-09-20 18:09:25.256571052 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/strcat-chk.c	2004-09-20 18:09:25.256571052 +0200
@@ -0,0 +1,204 @@
+/* Copyright (C) 2004  Free Software Foundation.
+
+   Ensure builtin __strcat_chk performs correctly.  */
+
+extern void abort (void);
+typedef __SIZE_TYPE__ size_t;
+extern size_t strlen(const char *);
+extern void *memcpy (void *, const void *, size_t);
+extern char *strcat (char *, const char *);
+extern int memcmp (const void *, const void *, size_t);
+extern char *strcpy (char *, const char *);
+extern int strcmp (const char *, const char *);
+extern void *memset (void *, int, size_t);
+#define RESET_DST_WITH(FILLER) \
+  do { memset (dst, 'X', sizeof (dst)); strcpy (dst, (FILLER)); } while (0)
+
+#include "chk.h"
+
+const char s1[] = "123";
+char p[32] = "";
+char *s2 = "defg";
+char *s3 = "FGH";
+char *s4;
+size_t l1 = 1;
+char *s5;
+
+void
+__attribute__((noinline))
+test1 (void)
+{
+  const char *const x1 = "hello world";
+  const char *const x2 = "";
+  char dst[64], *d2;
+
+  chk_calls = 0;
+  strcat_disallowed = 1;
+  /* Following strcat calls should be optimized out at compile time.  */  
+  RESET_DST_WITH (x1);
+  if (strcat (dst, "") != dst || strcmp (dst, x1))
+    abort ();
+  RESET_DST_WITH (x1);
+  if (strcat (dst, x2) != dst || strcmp (dst, x1))
+    abort ();
+  RESET_DST_WITH (x1); d2 = dst;
+  if (strcat (++d2, x2) != dst+1 || d2 != dst+1 || strcmp (dst, x1))
+    abort ();
+  RESET_DST_WITH (x1); d2 = dst;
+  if (strcat (++d2+5, x2) != dst+6 || d2 != dst+1 || strcmp (dst, x1))
+    abort ();
+  RESET_DST_WITH (x1); d2 = dst;
+  if (strcat (++d2+5, x1+11) != dst+6 || d2 != dst+1 || strcmp (dst, x1))
+    abort ();
+  if (chk_calls)
+    abort ();
+  strcat_disallowed = 0;
+
+  RESET_DST_WITH (x1);
+  if (strcat (dst, " 1111") != dst
+      || memcmp (dst, "hello world 1111\0XXX", 20))
+    abort ();
+  
+  RESET_DST_WITH (x1);
+  if (strcat (dst+5, " 2222") != dst+5
+      || memcmp (dst, "hello world 2222\0XXX", 20))
+    abort ();
+  
+  RESET_DST_WITH (x1); d2 = dst;
+  if (strcat (++d2+5, " 3333") != dst+6 || d2 != dst+1
+      || memcmp (dst, "hello world 3333\0XXX", 20))
+    abort ();
+  
+  RESET_DST_WITH (x1);
+  strcat (strcat (strcat (strcat (strcat (strcat (dst, ": this "), ""),
+				  "is "), "a "), "test"), ".");
+  if (memcmp (dst, "hello world: this is a test.\0X", 30))
+    abort ();
+
+  chk_calls = 0;
+  strcat_disallowed = 1;
+  /* Test at least one instance of the __builtin_ style.  We do this
+     to ensure that it works and that the prototype is correct.  */
+  RESET_DST_WITH (x1);
+  if (__builtin_strcat (dst, "") != dst || strcmp (dst, x1))
+    abort ();
+  if (chk_calls)
+    abort ();
+  strcat_disallowed = 0;
+}
+
+
+/* Test whether compile time checking is done where it should
+   and so is runtime object size checking.  */
+void
+__attribute__((noinline))
+test2 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4];
+  char buf3[20];
+  int i;
+
+  /* The following calls should do runtime checking
+     - source length is not known, but destination is.  */
+  memset (&a, '\0', sizeof (a));
+  s5 = (char *) &a;
+  __asm __volatile ("" : : "r" (s5) : "memory");
+  chk_calls = 0;
+  strcat (a.buf1 + 2, s3 + 3);
+  strcat (r, s3 + 2);
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  memset (r, '\0', 3);
+  __asm __volatile ("" : : "r" (r) : "memory");
+  strcat (r, s2 + 2);
+  strcat (r + 2, s3 + 3);
+  r = buf3;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1];
+      else if (i == l1)
+	r = &a.buf2[7];
+      else if (i == l1 + 1)
+	r = &buf3[5];
+      else if (i == l1 + 2)
+	r = &a.buf1[9];
+    }
+  strcat (r, s2 + 4);
+  if (chk_calls != 5)
+    abort ();
+
+  /* Following have known destination and known source length,
+     but we don't know the length of dest string, so runtime checking
+     is needed too.  */
+  memset (&a, '\0', sizeof (a));
+  chk_calls = 0;
+  s5 = (char *) &a;
+  __asm __volatile ("" : : "r" (s5) : "memory");
+  strcat (a.buf1 + 2, "a");
+  strcat (r, "");
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  memset (r, '\0', 3);
+  __asm __volatile ("" : : "r" (r) : "memory");
+  strcat (r, s1 + 1);
+  if (chk_calls != 2)
+    abort ();
+  chk_calls = 0;
+  /* Unknown destination and source, no checking.  */
+  strcat (s4, s3);
+  if (chk_calls)
+    abort ();
+  chk_calls = 0;
+}
+
+/* Test whether runtime and/or compile time checking catches
+   buffer overflows.  */
+void
+__attribute__((noinline))
+test3 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char buf3[20];
+
+  memset (&a, '\0', sizeof (a));
+  memset (buf3, '\0', sizeof (buf3));
+  s5 = (char *) &a;
+  __asm __volatile ("" : : "r" (s5) : "memory");
+  s5 = buf3;
+  __asm __volatile ("" : : "r" (s5) : "memory");
+  chk_fail_allowed = 1;
+  /* Runtime checks.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      strcat (&a.buf2[9], s2 + 3);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      strcat (&a.buf2[7], s3 + strlen (s3) - 3);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      strcat (&buf3[19], "a");
+      abort ();
+    }
+  chk_fail_allowed = 0;
+}
+
+void
+main_test (void)
+{
+#ifndef __OPTIMIZE__
+  /* Object size checking is only intended for -O[s123].  */
+  return;
+#endif
+  __asm ("" : "=r" (s2) : "0" (s2));
+  __asm ("" : "=r" (s3) : "0" (s3));
+  __asm ("" : "=r" (l1) : "0" (l1));
+  s4 = p;
+  test1 ();
+  memset (p, '\0', sizeof (p));
+  test2 ();
+  test3 ();
+}
--- gcc/testsuite/gcc.c-torture/execute/builtins/memset-chk-lib.c.jj	2004-09-20 18:09:25.225576539 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/memset-chk-lib.c	2004-09-20 18:09:25.225576539 +0200
@@ -0,0 +1 @@
+#include "lib/chk.c"
--- gcc/testsuite/gcc.c-torture/execute/builtins/memmove-chk.c.jj	2004-09-20 18:09:25.276567512 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/memmove-chk.c	2004-09-20 18:09:25.276567512 +0200
@@ -0,0 +1,579 @@
+/* Copyright (C) 2004  Free Software Foundation.
+
+   Ensure builtin __memcpy_chk performs correctly.  */
+
+extern void abort (void);
+typedef __SIZE_TYPE__ size_t;
+extern size_t strlen(const char *);
+extern void *memcpy (void *, const void *, size_t);
+extern void *memmove (void *, const void *, size_t);
+extern int memcmp (const void *, const void *, size_t);
+
+#include "chk.h"
+
+const char s1[] = "123";
+char p[32] = "";
+char *s2 = "defg";
+char *s3 = "FGH";
+size_t l1 = 1;
+
+void
+__attribute__((noinline))
+test1 (void)
+{
+  int i;
+
+#if defined __i386__ || defined __x86_64__
+  /* The functions below might not be optimized into direct stores on all
+     arches.  It depends on how many instructions would be generated and
+     what limits the architecture chooses in STORE_BY_PIECES_P.  */
+  memmove_disallowed = 1;
+  memcpy_disallowed = 1;
+#endif
+
+  /* All the memmove calls in this routine except last have fixed length, so
+     object size checking should be done at compile time if optimizing.  */
+  chk_calls = 0;
+
+  if (memmove (p, "ABCDE", 6) != p || memcmp (p, "ABCDE", 6))
+    abort ();
+  if (memmove (p + 16, "VWX" + 1, 2) != p + 16
+      || memcmp (p + 16, "WX\0\0", 5))
+    abort ();
+  if (memmove (p + 1, "", 1) != p + 1 || memcmp (p, "A\0CDE", 6))
+    abort ();
+  if (memmove (p + 3, "FGHI", 4) != p + 3 || memcmp (p, "A\0CFGHI", 8))
+    abort ();
+
+  i = 8;
+  memmove (p + 20, "qrstu", 6);
+  memmove (p + 25, "QRSTU", 6);
+  if (memmove (p + 25 + 1, s1, 3) != p + 25 + 1
+      || memcmp (p + 25, "Q123U", 6))
+    abort ();
+
+  if (memmove (memmove (p, "abcdEFG", 4) + 4, "efg", 4) != p + 4
+      || memcmp (p, "abcdefg", 8))
+    abort();
+
+  /* Test at least one instance of the __builtin_ style.  We do this
+     to ensure that it works and that the prototype is correct.  */
+  if (__builtin_memmove (p, "ABCDE", 6) != p || memcmp (p, "ABCDE", 6))
+    abort ();
+
+  memmove (p + 5, s3, 1);
+  if (memcmp (p, "ABCDEFg", 8))
+    abort ();
+
+  memmove_disallowed = 0;
+  memcpy_disallowed = 0;
+  if (chk_calls)
+    abort ();
+  chk_calls = 0;
+
+  memmove (p + 6, s1 + 1, l1);
+  if (memcmp (p, "ABCDEF2", 8))
+    abort ();
+
+  /* The above memmove copies into an object with known size, but
+     unknown length, so it should be a __memmove_chk call.  */
+  if (chk_calls != 1)
+    abort ();
+}
+
+long buf1[64];
+char *buf2 = (char *) (buf1 + 32);
+long buf5[20];
+char buf7[20];
+
+void
+__attribute__((noinline))
+test2_sub (long *buf3, char *buf4, char *buf6, int n)
+{
+  int i = 0;
+
+  /* All the memmove/__builtin_memmove/__builtin___memmove_chk
+     calls in this routine are either fixed length, or have
+     side-effects in __builtin_object_size arguments, or
+     dst doesn't point into a known object.  */
+  chk_calls = 0;
+
+  /* These should probably be handled by store_by_pieces on most arches.  */
+  if (memmove (buf1, "ABCDEFGHI", 9) != (char *) buf1
+      || memcmp (buf1, "ABCDEFGHI\0", 11))
+    abort ();
+
+  if (memmove (buf1, "abcdefghijklmnopq", 17) != (char *) buf1
+      || memcmp (buf1, "abcdefghijklmnopq\0", 19))
+    abort ();
+
+  if (__builtin_memmove (buf3, "ABCDEF", 6) != (char *) buf1
+      || memcmp (buf1, "ABCDEFghijklmnopq\0", 19))
+    abort ();
+
+  if (__builtin_memmove (buf3, "a", 1) != (char *) buf1
+      || memcmp (buf1, "aBCDEFghijklmnopq\0", 19))
+    abort ();
+
+  if (memmove ((char *) buf3 + 2, "bcd" + ++i, 2) != (char *) buf1 + 2
+      || memcmp (buf1, "aBcdEFghijklmnopq\0", 19)
+      || i != 1)
+    abort ();
+
+  /* These should probably be handled by move_by_pieces on most arches.  */
+  if (memmove ((char *) buf3 + 4, buf5, 6) != (char *) buf1 + 4
+      || memcmp (buf1, "aBcdRSTUVWklmnopq\0", 19))
+    abort ();
+
+  if (__builtin_memmove ((char *) buf1 + ++i + 8, (char *) buf5 + 1, 1)
+      != (char *) buf1 + 10
+      || memcmp (buf1, "aBcdRSTUVWSlmnopq\0", 19)
+      || i != 2)
+    abort ();
+
+  if (memmove ((char *) buf3 + 14, buf6, 2) != (char *) buf1 + 14
+      || memcmp (buf1, "aBcdRSTUVWSlmnrsq\0", 19))
+    abort ();
+
+  if (memmove (buf3, buf5, 8) != (char *) buf1
+      || memcmp (buf1, "RSTUVWXYVWSlmnrsq\0", 19))
+    abort ();
+
+  if (memmove (buf3, buf5, 17) != (char *) buf1
+      || memcmp (buf1, "RSTUVWXYZ01234567\0", 19))
+    abort ();
+
+  __builtin_memmove (buf3, "aBcdEFghijklmnopq\0", 19);
+
+  /* These should be handled either by movmemendM or memmove
+     call.  */
+
+  /* buf3 points to an unknown object, so __memmove_chk should not be done.  */
+  if (memmove ((char *) buf3 + 4, buf5, n + 6) != (char *) buf1 + 4
+      || memcmp (buf1, "aBcdRSTUVWklmnopq\0", 19))
+    abort ();
+
+  /* This call has side-effects in dst, therefore no checking.  */
+  if (__builtin___memmove_chk ((char *) buf1 + ++i + 8, (char *) buf5 + 1,
+			       n + 1, os ((char *) buf1 + ++i + 8))
+      != (char *) buf1 + 11
+      || memcmp (buf1, "aBcdRSTUVWkSmnopq\0", 19)
+      || i != 3)
+    abort ();
+
+  if (memmove ((char *) buf3 + 14, buf6, n + 2) != (char *) buf1 + 14
+      || memcmp (buf1, "aBcdRSTUVWkSmnrsq\0", 19))
+    abort ();
+
+  i = 1;
+
+  /* These might be handled by store_by_pieces.  */
+  if (memmove (buf2, "ABCDEFGHI", 9) != buf2
+      || memcmp (buf2, "ABCDEFGHI\0", 11))
+    abort ();
+
+  if (memmove (buf2, "abcdefghijklmnopq", 17) != buf2
+      || memcmp (buf2, "abcdefghijklmnopq\0", 19))
+    abort ();
+
+  if (__builtin_memmove (buf4, "ABCDEF", 6) != buf2
+      || memcmp (buf2, "ABCDEFghijklmnopq\0", 19))
+    abort ();
+
+  if (__builtin_memmove (buf4, "a", 1) != buf2
+      || memcmp (buf2, "aBCDEFghijklmnopq\0", 19))
+    abort ();
+
+  if (memmove (buf4 + 2, "bcd" + i++, 2) != buf2 + 2
+      || memcmp (buf2, "aBcdEFghijklmnopq\0", 19)
+      || i != 2)
+    abort ();
+
+  /* These might be handled by move_by_pieces.  */
+  if (memmove (buf4 + 4, buf7, 6) != buf2 + 4
+      || memcmp (buf2, "aBcdRSTUVWklmnopq\0", 19))
+    abort ();
+
+  /* Side effect.  */
+  if (__builtin___memmove_chk (buf2 + i++ + 8, buf7 + 1, 1,
+			       os (buf2 + i++ + 8))
+      != buf2 + 10
+      || memcmp (buf2, "aBcdRSTUVWSlmnopq\0", 19)
+      || i != 3)
+    abort ();
+
+  if (memmove (buf4 + 14, buf6, 2) != buf2 + 14
+      || memcmp (buf2, "aBcdRSTUVWSlmnrsq\0", 19))
+    abort ();
+
+  __builtin_memmove (buf4, "aBcdEFghijklmnopq\0", 19);
+
+  /* These should be handled either by movmemendM or memmove
+     call.  */
+  if (memmove (buf4 + 4, buf7, n + 6) != buf2 + 4
+      || memcmp (buf2, "aBcdRSTUVWklmnopq\0", 19))
+    abort ();
+
+  /* Side effect.  */
+  if (__builtin___memmove_chk (buf2 + i++ + 8, buf7 + 1, n + 1,
+			       os (buf2 + i++ + 8))
+      != buf2 + 11
+      || memcmp (buf2, "aBcdRSTUVWkSmnopq\0", 19)
+      || i != 4)
+    abort ();
+
+  if (memmove (buf4 + 14, buf6, n + 2) != buf2 + 14
+      || memcmp (buf2, "aBcdRSTUVWkSmnrsq\0", 19))
+    abort ();
+
+  if (chk_calls)
+    abort ();
+}
+
+void
+__attribute__((noinline))
+test2 (void)
+{
+  long *x;
+  char *y;
+  int z;
+  __builtin_memmove (buf5, "RSTUVWXYZ0123456789", 20);
+  __builtin_memmove (buf7, "RSTUVWXYZ0123456789", 20);
+ __asm ("" : "=r" (x) : "0" (buf1));
+ __asm ("" : "=r" (y) : "0" (buf2));
+ __asm ("" : "=r" (z) : "0" (0));
+  test2_sub (x, y, "rstuvwxyz", z);
+}
+
+static const struct foo
+{
+  char *s;
+  double d;
+  long l;
+} foo[] =
+{
+  { "hello world1", 3.14159, 101L },
+  { "hello world2", 3.14159, 102L },
+  { "hello world3", 3.14159, 103L },
+  { "hello world4", 3.14159, 104L },
+  { "hello world5", 3.14159, 105L },
+  { "hello world6", 3.14159, 106L }
+};
+
+static const struct bar
+{
+  char *s;
+  const struct foo f[3];
+} bar[] =
+{
+  {
+    "hello world10",
+    {
+      { "hello1", 3.14159, 201L },
+      { "hello2", 3.14159, 202L },
+      { "hello3", 3.14159, 203L },
+    }
+  },
+  {
+    "hello world11",
+    {
+      { "hello4", 3.14159, 204L },
+      { "hello5", 3.14159, 205L },
+      { "hello6", 3.14159, 206L },
+    }
+  }
+};
+
+static const int baz[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
+
+void
+__attribute__((noinline))
+test3 (void)
+{
+  const char *s;
+  struct foo f1[sizeof foo/sizeof*foo];
+  struct bar b1[sizeof bar/sizeof*bar];
+  int bz[sizeof baz/sizeof*baz];
+
+  /* All the memmove/__builtin_memmove calls in this routine have fixed
+     length.  */
+  chk_calls = 0;
+
+  /* All the *memmove calls below have src in read-only memory, so all
+     of them should be optimized into memcpy.  */
+  memmove_disallowed = 1;
+  if (memmove (f1, foo, sizeof (foo)) != f1 || memcmp (f1, foo, sizeof (foo)))
+    abort ();
+  if (memmove (b1, bar, sizeof (bar)) != b1 || memcmp (b1, bar, sizeof (bar)))
+    abort ();
+  memmove (bz, baz, sizeof (baz));
+  if (memcmp (bz, baz, sizeof (baz)))
+    abort ();
+
+  if (memmove (p, "abcde", 6) != p || memcmp (p, "abcde", 6))
+    abort ();
+  s = s1;
+  if (memmove (p + 2, ++s, 0) != p + 2 || memcmp (p, "abcde", 6) || s != s1 + 1)
+    abort ();
+  if (__builtin_memmove (p + 3, "", 1) != p + 3 || memcmp (p, "abc\0e", 6))
+    abort ();
+  memmove (p + 2, "fghijk", 4);
+  if (memcmp (p, "abfghi", 7))
+    abort ();
+  s = s1 + 1;
+  memmove (p + 1, s++, 0);
+  if (memcmp (p, "abfghi", 7) || s != s1 + 2)
+    abort ();
+  __builtin_memmove (p + 4, "ABCDE", 1);
+  if (memcmp (p, "abfgAi", 7))
+    abort ();
+
+  /* memmove with length 1 can be optimized into memcpy if it can be
+     expanded inline.  */
+  if (memmove (p + 2, p + 3, 1) != p + 2)
+    abort ();
+  if (memcmp (p, "abggAi", 7))
+    abort ();
+
+  if (chk_calls)
+    abort ();
+  memmove_disallowed = 0;
+}
+
+/* Test whether compile time checking is done where it should
+   and so is runtime object size checking.  */
+void
+__attribute__((noinline))
+test4 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4];
+  char buf3[20];
+  int i;
+  size_t l;
+
+  /* The following calls should do runtime checking
+     - length is not known, but destination is.  */
+  chk_calls = 0;
+  memmove (a.buf1 + 2, s3, l1);
+  memmove (r, s3, l1 + 1);
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  memmove (r, s2, l1 + 2);
+  memmove (r + 2, s3, l1);
+  r = buf3;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1];
+      else if (i == l1)
+	r = &a.buf2[7];
+      else if (i == l1 + 1)
+	r = &buf3[5];
+      else if (i == l1 + 2)
+	r = &a.buf1[9];
+    }
+  memmove (r, s2, l1);
+  if (chk_calls != 5)
+    abort ();
+
+  /* Following have known destination and known length,
+     so if optimizing certainly shouldn't result in the checking
+     variants.  */
+  chk_calls = 0;
+  memmove (a.buf1 + 2, s3, 1);
+  memmove (r, s3, 2);
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  memmove (r, s2, 3);
+  r = buf3;
+  l = 4;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1], l = 2;
+      else if (i == l1)
+	r = &a.buf2[7], l = 3;
+      else if (i == l1 + 1)
+	r = &buf3[5], l = 4;
+      else if (i == l1 + 2)
+	r = &a.buf1[9], l = 1;
+    }
+  memmove (r, s2, 1);
+  /* Here, l is known to be at most 4 and __builtin_object_size (&buf3[16], 0)
+     is 4, so this doesn't need runtime checking.  */
+  memmove (&buf3[16], s2, l);
+  if (chk_calls)
+    abort ();
+  chk_calls = 0;
+}
+
+/* Test whether runtime and/or compile time checking catches
+   buffer overflows.  */
+void
+__attribute__((noinline))
+test5 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char buf3[20];
+
+  chk_fail_allowed = 1;
+  /* Runtime checks.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      memmove (&a.buf2[9], s2, l1 + 1);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      memmove (&a.buf2[7], s3, strlen (s3) + 1);
+      abort ();
+    }
+  /* This should be detectable at compile time already.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      memmove (&buf3[19], "ab", 2);
+      abort ();
+    }
+  chk_fail_allowed = 0;
+}
+
+#ifndef MAX_OFFSET
+#define MAX_OFFSET (sizeof (long long))
+#endif
+
+#ifndef MAX_COPY
+#define MAX_COPY (10 * sizeof (long long))
+#endif
+
+#ifndef MAX_EXTRA
+#define MAX_EXTRA (sizeof (long long))
+#endif
+
+#define MAX_LENGTH (MAX_OFFSET + MAX_COPY + MAX_EXTRA)
+
+/* Use a sequence length that is not divisible by two, to make it more
+   likely to detect when words are mixed up.  */
+#define SEQUENCE_LENGTH 31
+
+static union {
+  char buf[MAX_LENGTH];
+  long long align_int;
+  long double align_fp;
+} u1, u2;
+
+void
+__attribute__((noinline))
+test6 (void)
+{
+  int off1, off2, len, i;
+  char *p, *q, c;
+
+  for (off1 = 0; off1 < MAX_OFFSET; off1++)
+    for (off2 = 0; off2 < MAX_OFFSET; off2++)
+      for (len = 1; len < MAX_COPY; len++)
+	{
+	  for (i = 0, c = 'A'; i < MAX_LENGTH; i++, c++)
+	    {
+	      u1.buf[i] = 'a';
+	      if (c >= 'A' + SEQUENCE_LENGTH)
+		c = 'A';
+	      u2.buf[i] = c;
+	    }
+
+	  p = memmove (u1.buf + off1, u2.buf + off2, len);
+	  if (p != u1.buf + off1)
+	    abort ();
+
+	  q = u1.buf;
+	  for (i = 0; i < off1; i++, q++)
+	    if (*q != 'a')
+	      abort ();
+
+	  for (i = 0, c = 'A' + off2; i < len; i++, q++, c++)
+	    {
+	      if (c >= 'A' + SEQUENCE_LENGTH)
+		c = 'A';
+	      if (*q != c)
+		abort ();
+	    }
+
+	  for (i = 0; i < MAX_EXTRA; i++, q++)
+	    if (*q != 'a')
+	      abort ();
+	}
+}
+
+#define TESTSIZE 80
+
+char srcb[TESTSIZE] __attribute__ ((aligned));
+char dstb[TESTSIZE] __attribute__ ((aligned));
+
+void
+__attribute__((noinline))
+check (char *test, char *match, int n)
+{
+  if (memcmp (test, match, n))
+    abort ();
+}
+
+#define TN(n) \
+{ memset (dstb, 0, n); memmove (dstb, srcb, n); check (dstb, srcb, n); }
+#define T(n) \
+TN (n) \
+TN ((n) + 1) \
+TN ((n) + 2) \
+TN ((n) + 3)
+
+void
+__attribute__((noinline))
+test7 (void)
+{
+  int i;
+
+  chk_calls = 0;
+
+  for (i = 0; i < sizeof (srcb); ++i)
+      srcb[i] = 'a' + i % 26;
+
+  T (0);
+  T (4);
+  T (8);
+  T (12);
+  T (16);
+  T (20);
+  T (24);
+  T (28);
+  T (32);
+  T (36);
+  T (40);
+  T (44);
+  T (48);
+  T (52);
+  T (56);
+  T (60);
+  T (64);
+  T (68);
+  T (72);
+  T (76);
+
+  /* All memmove calls in this routine have constant arguments.  */
+  if (chk_calls)
+    abort ();
+}
+
+void
+main_test (void)
+{
+#ifndef __OPTIMIZE__
+  /* Object size checking is only intended for -O[s123].  */
+  return;
+#endif
+  __asm ("" : "=r" (l1) : "0" (l1));
+  test1 ();
+  test2 ();
+  __builtin_memset (p, '\0', sizeof (p));
+  test3 ();
+  test4 ();
+  test5 ();
+  test6 ();
+  test7 ();
+}
--- gcc/testsuite/gcc.c-torture/execute/builtins/vsprintf-chk.c.jj	2004-09-20 18:09:25.274567866 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/vsprintf-chk.c	2004-09-20 18:09:25.274567866 +0200
@@ -0,0 +1,290 @@
+/* Copyright (C) 2004  Free Software Foundation.
+
+   Ensure builtin __vsprintf_chk performs correctly.  */
+
+#include <stdarg.h>
+
+extern void abort (void);
+typedef __SIZE_TYPE__ size_t;
+extern size_t strlen(const char *);
+extern void *memcpy (void *, const void *, size_t);
+extern char *strcpy (char *, const char *);
+extern int memcmp (const void *, const void *, size_t);
+extern void *memset (void *, int, size_t);
+extern int vsprintf (char *, const char *, va_list);
+
+#include "chk.h"
+
+const char s1[] = "123";
+char p[32] = "";
+char *s2 = "defg";
+char *s3 = "FGH";
+char *s4;
+size_t l1 = 1;
+static char buffer[32];
+char *ptr = "barf";
+
+int
+__attribute__((noinline))
+test1_sub (int i, ...)
+{
+  int ret = 0;
+  va_list ap;
+  va_start (ap, i);
+  switch (i)
+    {
+    case 0:
+      vsprintf (buffer, "foo", ap);
+      break;
+    case 1:
+      ret = vsprintf (buffer, "foo", ap);
+      break;
+    case 2:
+      vsprintf (buffer, "%s", ap);
+      break;
+    case 3:
+      ret = vsprintf (buffer, "%s", ap);
+      break;
+    case 4:
+      vsprintf (buffer, "%d - %c", ap);
+      break;
+    case 5:
+      vsprintf (s4, "%d - %c", ap);
+      break;
+    }
+  va_end (ap);
+  return ret;
+}
+
+void
+__attribute__((noinline))
+test1 (void)
+{
+  chk_calls = 0;
+  vsprintf_disallowed = 1;
+
+  memset (buffer, 'A', 32);
+  test1_sub (0);
+  if (memcmp (buffer, "foo", 4) || buffer[4] != 'A')
+    abort ();
+
+  memset (buffer, 'A', 32);
+  if (test1_sub (1) != 3)
+    abort ();
+  if (memcmp (buffer, "foo", 4) || buffer[4] != 'A')
+    abort ();
+
+  if (chk_calls)
+    abort ();
+  vsprintf_disallowed = 0;
+
+  memset (buffer, 'A', 32);
+  test1_sub (2, "bar");
+  if (memcmp (buffer, "bar", 4) || buffer[4] != 'A')
+    abort ();
+
+  memset (buffer, 'A', 32);
+  if (test1_sub (3, "bar") != 3)
+    abort ();
+  if (memcmp (buffer, "bar", 4) || buffer[4] != 'A')
+    abort ();
+
+  memset (buffer, 'A', 32);
+  test1_sub (2, ptr);
+  if (memcmp (buffer, "barf", 5) || buffer[5] != 'A')
+    abort ();
+
+  memset (buffer, 'A', 32);
+  test1_sub (4, (int) l1 + 27, *ptr);
+  if (memcmp (buffer, "28 - b\0AAAAA", 12))
+    abort ();
+
+  if (chk_calls != 4)
+    abort ();
+  chk_calls = 0;
+
+  test1_sub (5, (int) l1 - 17, ptr[1]);
+  if (memcmp (s4, "-16 - a", 8))
+    abort ();
+  if (chk_calls)
+    abort ();
+}
+
+void
+__attribute__((noinline))
+test2_sub (int i, ...)
+{
+  va_list ap;
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4];
+  char buf3[20];
+  int j;
+
+  va_start (ap, i);
+  /* The following calls should do runtime checking
+     - source length is not known, but destination is.  */
+  switch (i)
+    {
+    case 0:
+      vsprintf (a.buf1 + 2, "%s", ap);
+      break;
+    case 1:
+      vsprintf (r, "%s%c", ap);
+      break;
+    case 2:
+      r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+      vsprintf (r, "%c %s", ap);
+      break;
+    case 3:
+      r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+      vsprintf (r + 2, s3 + 3, ap);
+      break;
+    case 4:
+    case 7:
+      r = buf3;
+      for (j = 0; j < 4; ++j)
+	{
+	  if (j == l1 - 1)
+	    r = &a.buf1[1];
+	  else if (j == l1)
+	    r = &a.buf2[7];
+	  else if (j == l1 + 1)
+	    r = &buf3[5];
+	  else if (j == l1 + 2)
+	    r = &a.buf1[9];
+	}
+      if (i == 4)
+	vsprintf (r, s2 + 4, ap);
+      else
+	vsprintf (r, "a", ap);
+      break;
+    case 5:
+      r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+      vsprintf (r, "%s", ap);
+      break;
+    case 6:
+      vsprintf (a.buf1 + 2, "", ap);
+      break;
+    case 8:
+      vsprintf (s4, "%s %d", ap);
+      break;
+    }
+  va_end (ap);
+}
+
+/* Test whether compile time checking is done where it should
+   and so is runtime object size checking.  */
+void
+__attribute__((noinline))
+test2 (void)
+{
+  /* The following calls should do runtime checking
+     - source length is not known, but destination is.  */
+  chk_calls = 0;
+  test2_sub (0, s3 + 3);
+  test2_sub (1, s3 + 4, s3[3]);
+  test2_sub (2, s2[2], s2 + 4);
+  test2_sub (3);
+  test2_sub (4);
+  test2_sub (5, s1 + 1);
+  if (chk_calls != 6)
+    abort ();
+
+  /* Following have known destination and known source length,
+     so if optimizing certainly shouldn't result in the checking
+     variants.  */
+  chk_calls = 0;
+  vsprintf_disallowed = 1;
+  test2_sub (6);
+  test2_sub (7);
+  vsprintf_disallowed = 0;
+  /* Unknown destination and source, no checking.  */
+  test2_sub (8, s3, 0);
+  if (chk_calls)
+    abort ();
+}
+
+void
+__attribute__((noinline))
+test3_sub (int i, ...)
+{
+  va_list ap;
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char buf3[20];
+
+  va_start (ap, i);
+  /* The following calls should do runtime checking
+     - source length is not known, but destination is.  */
+  switch (i)
+    {
+    case 0:
+      vsprintf (&a.buf2[9], "%c%s", ap);
+      break;
+    case 1:
+      vsprintf (&a.buf2[7], "%s%c", ap);
+      break;
+    case 2:
+      vsprintf (&a.buf2[7], "%d", ap);
+      break;
+    case 3:
+      vsprintf (&buf3[17], "%s", ap);
+      break;
+    case 4:
+      vsprintf (&buf3[19], "a", ap);
+      break;
+    }
+  va_end (ap);
+}
+
+/* Test whether runtime and/or compile time checking catches
+   buffer overflows.  */
+void
+__attribute__((noinline))
+test3 (void)
+{
+  chk_fail_allowed = 1;
+  /* Runtime checks.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      test3_sub (0, s2[3], s2 + 4);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      test3_sub (1, s3 + strlen (s3) - 2, *s3);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      test3_sub (2, (int) l1 + 9999);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      test3_sub (3, "abc");
+      abort ();
+    }
+  /* This should be detectable at compile time already.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      test3_sub (4);
+      abort ();
+    }
+  chk_fail_allowed = 0;
+}
+
+void
+main_test (void)
+{
+#ifndef __OPTIMIZE__
+  /* Object size checking is only intended for -O[s123].  */
+  return;
+#endif
+  __asm ("" : "=r" (s2) : "0" (s2));
+  __asm ("" : "=r" (s3) : "0" (s3));
+  __asm ("" : "=r" (l1) : "0" (l1));
+  s4 = p;
+  test1 ();
+  test2 ();
+  test3 ();
+}
--- gcc/testsuite/gcc.c-torture/execute/builtins/strcpy-chk-lib.c.jj	2004-09-20 18:09:25.247572645 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/strcpy-chk-lib.c	2004-09-20 18:09:25.247572645 +0200
@@ -0,0 +1 @@
+#include "lib/chk.c"
--- gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk.c.jj	2004-09-20 18:09:25.226576362 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk.c	2004-09-20 18:09:25.227576185 +0200
@@ -0,0 +1,487 @@
+/* Copyright (C) 2004  Free Software Foundation.
+
+   Ensure builtin __mempcpy_chk performs correctly.  */
+
+extern void abort (void);
+typedef __SIZE_TYPE__ size_t;
+extern size_t strlen(const char *);
+extern void *memcpy (void *, const void *, size_t);
+extern void *mempcpy (void *, const void *, size_t);
+extern int memcmp (const void *, const void *, size_t);
+
+#include "chk.h"
+
+const char s1[] = "123";
+char p[32] = "";
+char *s2 = "defg";
+char *s3 = "FGH";
+size_t l1 = 1;
+
+void
+__attribute__((noinline))
+test1 (void)
+{
+  int i;
+
+#if defined __i386__ || defined __x86_64__
+  /* The functions below might not be optimized into direct stores on all
+     arches.  It depends on how many instructions would be generated and
+     what limits the architecture chooses in STORE_BY_PIECES_P.  */
+  mempcpy_disallowed = 1;
+#endif
+
+  /* All the mempcpy calls in this routine except last have fixed length, so
+     object size checking should be done at compile time if optimizing.  */
+  chk_calls = 0;
+
+  if (mempcpy (p, "ABCDE", 6) != p + 6 || memcmp (p, "ABCDE", 6))
+    abort ();
+  if (mempcpy (p + 16, "VWX" + 1, 2) != p + 16 + 2
+      || memcmp (p + 16, "WX\0\0", 5))
+    abort ();
+  if (mempcpy (p + 1, "", 1) != p + 1 + 1 || memcmp (p, "A\0CDE", 6))
+    abort ();
+  if (mempcpy (p + 3, "FGHI", 4) != p + 3 + 4 || memcmp (p, "A\0CFGHI", 8))
+    abort ();
+
+  i = 8;
+  memcpy (p + 20, "qrstu", 6);
+  memcpy (p + 25, "QRSTU", 6);
+  if (mempcpy (p + 25 + 1, s1, 3) != (p + 25 + 1 + 3)
+      || memcmp (p + 25, "Q123U", 6))
+    abort ();
+
+  if (mempcpy (mempcpy (p, "abcdEFG", 4), "efg", 4) != p + 8
+      || memcmp (p, "abcdefg", 8))
+    abort();
+
+  /* Test at least one instance of the __builtin_ style.  We do this
+     to ensure that it works and that the prototype is correct.  */
+  if (__builtin_mempcpy (p, "ABCDE", 6) != p + 6 || memcmp (p, "ABCDE", 6))
+    abort ();
+
+  /* If the result of mempcpy is ignored, gcc should use memcpy.
+     This should be optimized always, so disallow mempcpy calls.  */
+  mempcpy_disallowed = 1;
+  mempcpy (p + 5, s3, 1);
+  if (memcmp (p, "ABCDEFg", 8))
+    abort ();
+
+  if (chk_calls)
+    abort ();
+  chk_calls = 0;
+
+  mempcpy (p + 6, s1 + 1, l1);
+  if (memcmp (p, "ABCDEF2", 8))
+    abort ();
+
+  /* The above mempcpy copies into an object with known size, but
+     unknown length and with result ignored, so it should be a
+     __memcpy_chk call.  */
+  if (chk_calls != 1)
+    abort ();
+
+  mempcpy_disallowed = 0;
+}
+
+long buf1[64];
+char *buf2 = (char *) (buf1 + 32);
+long buf5[20];
+char buf7[20];
+
+void
+__attribute__((noinline))
+test2_sub (long *buf3, char *buf4, char *buf6, int n)
+{
+  int i = 0;
+
+  /* All the mempcpy/__builtin_mempcpy/__builtin___mempcpy_chk
+     calls in this routine are either fixed length, or have
+     side-effects in __builtin_object_size arguments, or
+     dst doesn't point into a known object.  */
+  chk_calls = 0;
+
+  /* These should probably be handled by store_by_pieces on most arches.  */
+  if (mempcpy (buf1, "ABCDEFGHI", 9) != (char *) buf1 + 9
+      || memcmp (buf1, "ABCDEFGHI\0", 11))
+    abort ();
+
+  if (mempcpy (buf1, "abcdefghijklmnopq", 17) != (char *) buf1 + 17
+      || memcmp (buf1, "abcdefghijklmnopq\0", 19))
+    abort ();
+
+  if (__builtin_mempcpy (buf3, "ABCDEF", 6) != (char *) buf1 + 6
+      || memcmp (buf1, "ABCDEFghijklmnopq\0", 19))
+    abort ();
+
+  if (__builtin_mempcpy (buf3, "a", 1) != (char *) buf1 + 1
+      || memcmp (buf1, "aBCDEFghijklmnopq\0", 19))
+    abort ();
+
+  if (mempcpy ((char *) buf3 + 2, "bcd" + ++i, 2) != (char *) buf1 + 4
+      || memcmp (buf1, "aBcdEFghijklmnopq\0", 19)
+      || i != 1)
+    abort ();
+
+  /* These should probably be handled by move_by_pieces on most arches.  */
+  if (mempcpy ((char *) buf3 + 4, buf5, 6) != (char *) buf1 + 10
+      || memcmp (buf1, "aBcdRSTUVWklmnopq\0", 19))
+    abort ();
+
+  if (__builtin_mempcpy ((char *) buf1 + ++i + 8, (char *) buf5 + 1, 1)
+      != (char *) buf1 + 11
+      || memcmp (buf1, "aBcdRSTUVWSlmnopq\0", 19)
+      || i != 2)
+    abort ();
+
+  if (mempcpy ((char *) buf3 + 14, buf6, 2) != (char *) buf1 + 16
+      || memcmp (buf1, "aBcdRSTUVWSlmnrsq\0", 19))
+    abort ();
+
+  if (mempcpy (buf3, buf5, 8) != (char *) buf1 + 8
+      || memcmp (buf1, "RSTUVWXYVWSlmnrsq\0", 19))
+    abort ();
+
+  if (mempcpy (buf3, buf5, 17) != (char *) buf1 + 17
+      || memcmp (buf1, "RSTUVWXYZ01234567\0", 19))
+    abort ();
+
+  __builtin_memcpy (buf3, "aBcdEFghijklmnopq\0", 19);
+
+  /* These should be handled either by movmemendM or mempcpy
+     call.  */
+
+  /* buf3 points to an unknown object, so __mempcpy_chk should not be done.  */
+  if (mempcpy ((char *) buf3 + 4, buf5, n + 6) != (char *) buf1 + 10
+      || memcmp (buf1, "aBcdRSTUVWklmnopq\0", 19))
+    abort ();
+
+  /* This call has side-effects in dst, therefore no checking.  */
+  if (__builtin___mempcpy_chk ((char *) buf1 + ++i + 8, (char *) buf5 + 1,
+			       n + 1, os ((char *) buf1 + ++i + 8))
+      != (char *) buf1 + 12
+      || memcmp (buf1, "aBcdRSTUVWkSmnopq\0", 19)
+      || i != 3)
+    abort ();
+
+  if (mempcpy ((char *) buf3 + 14, buf6, n + 2) != (char *) buf1 + 16
+      || memcmp (buf1, "aBcdRSTUVWkSmnrsq\0", 19))
+    abort ();
+
+  i = 1;
+
+  /* These might be handled by store_by_pieces.  */
+  if (mempcpy (buf2, "ABCDEFGHI", 9) != buf2 + 9
+      || memcmp (buf2, "ABCDEFGHI\0", 11))
+    abort ();
+
+  if (mempcpy (buf2, "abcdefghijklmnopq", 17) != buf2 + 17
+      || memcmp (buf2, "abcdefghijklmnopq\0", 19))
+    abort ();
+
+  if (__builtin_mempcpy (buf4, "ABCDEF", 6) != buf2 + 6
+      || memcmp (buf2, "ABCDEFghijklmnopq\0", 19))
+    abort ();
+
+  if (__builtin_mempcpy (buf4, "a", 1) != buf2 + 1
+      || memcmp (buf2, "aBCDEFghijklmnopq\0", 19))
+    abort ();
+
+  if (mempcpy (buf4 + 2, "bcd" + i++, 2) != buf2 + 4
+      || memcmp (buf2, "aBcdEFghijklmnopq\0", 19)
+      || i != 2)
+    abort ();
+
+  /* These might be handled by move_by_pieces.  */
+  if (mempcpy (buf4 + 4, buf7, 6) != buf2 + 10
+      || memcmp (buf2, "aBcdRSTUVWklmnopq\0", 19))
+    abort ();
+
+  /* Side effect.  */
+  if (__builtin___mempcpy_chk (buf2 + i++ + 8, buf7 + 1, 1,
+			       os (buf2 + i++ + 8))
+      != buf2 + 11
+      || memcmp (buf2, "aBcdRSTUVWSlmnopq\0", 19)
+      || i != 3)
+    abort ();
+
+  if (mempcpy (buf4 + 14, buf6, 2) != buf2 + 16
+      || memcmp (buf2, "aBcdRSTUVWSlmnrsq\0", 19))
+    abort ();
+
+  __builtin_memcpy (buf4, "aBcdEFghijklmnopq\0", 19);
+
+  /* These should be handled either by movmemendM or mempcpy
+     call.  */
+  if (mempcpy (buf4 + 4, buf7, n + 6) != buf2 + 10
+      || memcmp (buf2, "aBcdRSTUVWklmnopq\0", 19))
+    abort ();
+
+  /* Side effect.  */
+  if (__builtin___mempcpy_chk (buf2 + i++ + 8, buf7 + 1,
+			       n + 1, os (buf2 + i++ + 8))
+      != buf2 + 12
+      || memcmp (buf2, "aBcdRSTUVWkSmnopq\0", 19)
+      || i != 4)
+    abort ();
+
+  if (mempcpy (buf4 + 14, buf6, n + 2) != buf2 + 16
+      || memcmp (buf2, "aBcdRSTUVWkSmnrsq\0", 19))
+    abort ();
+
+  if (chk_calls)
+    abort ();
+}
+
+void
+__attribute__((noinline))
+test2 (void)
+{
+  long *x;
+  char *y;
+  int z;
+  __builtin_memcpy (buf5, "RSTUVWXYZ0123456789", 20);
+  __builtin_memcpy (buf7, "RSTUVWXYZ0123456789", 20);
+ __asm ("" : "=r" (x) : "0" (buf1));
+ __asm ("" : "=r" (y) : "0" (buf2));
+ __asm ("" : "=r" (z) : "0" (0));
+  test2_sub (x, y, "rstuvwxyz", z);
+}
+
+volatile void *vx;
+
+/* Test whether compile time checking is done where it should
+   and so is runtime object size checking.  */
+void
+__attribute__((noinline))
+test3 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4];
+  char buf3[20];
+  int i;
+  size_t l;
+
+  /* The following calls should do runtime checking
+     - length is not known, but destination is.  */
+  chk_calls = 0;
+  vx = mempcpy (a.buf1 + 2, s3, l1);
+  vx = mempcpy (r, s3, l1 + 1);
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  vx = mempcpy (r, s2, l1 + 2);
+  vx = mempcpy (r + 2, s3, l1);
+  r = buf3;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1];
+      else if (i == l1)
+	r = &a.buf2[7];
+      else if (i == l1 + 1)
+	r = &buf3[5];
+      else if (i == l1 + 2)
+	r = &a.buf1[9];
+    }
+  vx = mempcpy (r, s2, l1);
+  if (chk_calls != 5)
+    abort ();
+
+  /* Following have known destination and known length,
+     so if optimizing certainly shouldn't result in the checking
+     variants.  */
+  chk_calls = 0;
+  vx = mempcpy (a.buf1 + 2, s3, 1);
+  vx = mempcpy (r, s3, 2);
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  vx = mempcpy (r, s2, 3);
+  r = buf3;
+  l = 4;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1], l = 2;
+      else if (i == l1)
+	r = &a.buf2[7], l = 3;
+      else if (i == l1 + 1)
+	r = &buf3[5], l = 4;
+      else if (i == l1 + 2)
+	r = &a.buf1[9], l = 1;
+    }
+  vx = mempcpy (r, s2, 1);
+  /* Here, l is known to be at most 4 and __builtin_object_size (&buf3[16], 0)
+     is 4, so this doesn't need runtime checking.  */
+  vx = mempcpy (&buf3[16], s2, l);
+  if (chk_calls)
+    abort ();
+  chk_calls = 0;
+}
+
+/* Test whether runtime and/or compile time checking catches
+   buffer overflows.  */
+void
+__attribute__((noinline))
+test4 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char buf3[20];
+
+  chk_fail_allowed = 1;
+  /* Runtime checks.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      vx = mempcpy (&a.buf2[9], s2, l1 + 1);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      vx = mempcpy (&a.buf2[7], s3, strlen (s3) + 1);
+      abort ();
+    }
+  /* This should be detectable at compile time already.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      vx = mempcpy (&buf3[19], "ab", 2);
+      abort ();
+    }
+  chk_fail_allowed = 0;
+}
+
+#ifndef MAX_OFFSET
+#define MAX_OFFSET (sizeof (long long))
+#endif
+
+#ifndef MAX_COPY
+#define MAX_COPY (10 * sizeof (long long))
+#endif
+
+#ifndef MAX_EXTRA
+#define MAX_EXTRA (sizeof (long long))
+#endif
+
+#define MAX_LENGTH (MAX_OFFSET + MAX_COPY + MAX_EXTRA)
+
+/* Use a sequence length that is not divisible by two, to make it more
+   likely to detect when words are mixed up.  */
+#define SEQUENCE_LENGTH 31
+
+static union {
+  char buf[MAX_LENGTH];
+  long long align_int;
+  long double align_fp;
+} u1, u2;
+
+void
+__attribute__((noinline))
+test5 (void)
+{
+  int off1, off2, len, i;
+  char *p, *q, c;
+
+  for (off1 = 0; off1 < MAX_OFFSET; off1++)
+    for (off2 = 0; off2 < MAX_OFFSET; off2++)
+      for (len = 1; len < MAX_COPY; len++)
+	{
+	  for (i = 0, c = 'A'; i < MAX_LENGTH; i++, c++)
+	    {
+	      u1.buf[i] = 'a';
+	      if (c >= 'A' + SEQUENCE_LENGTH)
+		c = 'A';
+	      u2.buf[i] = c;
+	    }
+
+	  p = mempcpy (u1.buf + off1, u2.buf + off2, len);
+	  if (p != u1.buf + off1 + len)
+	    abort ();
+
+	  q = u1.buf;
+	  for (i = 0; i < off1; i++, q++)
+	    if (*q != 'a')
+	      abort ();
+
+	  for (i = 0, c = 'A' + off2; i < len; i++, q++, c++)
+	    {
+	      if (c >= 'A' + SEQUENCE_LENGTH)
+		c = 'A';
+	      if (*q != c)
+		abort ();
+	    }
+
+	  for (i = 0; i < MAX_EXTRA; i++, q++)
+	    if (*q != 'a')
+	      abort ();
+	}
+}
+
+#define TESTSIZE 80
+
+char srcb[TESTSIZE] __attribute__ ((aligned));
+char dstb[TESTSIZE] __attribute__ ((aligned));
+
+void
+__attribute__((noinline))
+check (char *test, char *match, int n)
+{
+  if (memcmp (test, match, n))
+    abort ();
+}
+
+#define TN(n) \
+{ memset (dstb, 0, n); vx = mempcpy (dstb, srcb, n); check (dstb, srcb, n); }
+#define T(n) \
+TN (n) \
+TN ((n) + 1) \
+TN ((n) + 2) \
+TN ((n) + 3)
+
+void
+__attribute__((noinline))
+test6 (void)
+{
+  int i;
+
+  chk_calls = 0;
+
+  for (i = 0; i < sizeof (srcb); ++i)
+      srcb[i] = 'a' + i % 26;
+
+  T (0);
+  T (4);
+  T (8);
+  T (12);
+  T (16);
+  T (20);
+  T (24);
+  T (28);
+  T (32);
+  T (36);
+  T (40);
+  T (44);
+  T (48);
+  T (52);
+  T (56);
+  T (60);
+  T (64);
+  T (68);
+  T (72);
+  T (76);
+
+  /* All mempcpy calls in this routine have constant arguments.  */
+  if (chk_calls)
+    abort ();
+}
+
+void
+main_test (void)
+{
+#ifndef __OPTIMIZE__
+  /* Object size checking is only intended for -O[s123].  */
+  return;
+#endif
+  __asm ("" : "=r" (l1) : "0" (l1));
+  test1 ();
+  test2 ();
+  test3 ();
+  test4 ();
+  test5 ();
+  test6 ();
+}
--- gcc/testsuite/gcc.c-torture/execute/builtins/stpcpy-chk-lib.c.jj	2004-09-20 18:09:25.262569990 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/stpcpy-chk-lib.c	2004-09-20 18:09:25.262569990 +0200
@@ -0,0 +1 @@
+#include "lib/chk.c"
--- gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk-lib.c.jj	2004-09-20 18:09:25.261570167 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk-lib.c	2004-09-20 18:09:25.261570167 +0200
@@ -0,0 +1 @@
+#include "lib/chk.c"
--- gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk-lib.c.jj	2004-09-20 18:09:25.249572291 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk-lib.c	2004-09-20 18:09:25.249572291 +0200
@@ -0,0 +1 @@
+#include "lib/chk.c"
--- gcc/testsuite/gcc.c-torture/execute/builtins/memcpy-chk.c.jj	2004-09-20 18:09:25.265569459 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/memcpy-chk.c	2004-09-20 18:09:25.265569459 +0200
@@ -0,0 +1,479 @@
+/* Copyright (C) 2004  Free Software Foundation.
+
+   Ensure builtin __memcpy_chk performs correctly.  */
+
+extern void abort (void);
+typedef __SIZE_TYPE__ size_t;
+extern size_t strlen(const char *);
+extern void *memcpy (void *, const void *, size_t);
+extern int memcmp (const void *, const void *, size_t);
+
+#include "chk.h"
+
+const char s1[] = "123";
+char p[32] = "";
+char *s2 = "defg";
+char *s3 = "FGH";
+size_t l1 = 1;
+
+void
+__attribute__((noinline))
+test1 (void)
+{
+  int i;
+
+#if defined __i386__ || defined __x86_64__
+  /* The functions below might not be optimized into direct stores on all
+     arches.  It depends on how many instructions would be generated and
+     what limits the architecture chooses in STORE_BY_PIECES_P.  */
+  memcpy_disallowed = 1;
+#endif
+
+  /* All the memcpy calls in this routine except last have fixed length, so
+     object size checking should be done at compile time if optimizing.  */
+  chk_calls = 0;
+
+  if (memcpy (p, "ABCDE", 6) != p || memcmp (p, "ABCDE", 6))
+    abort ();
+  if (memcpy (p + 16, "VWX" + 1, 2) != p + 16
+      || memcmp (p + 16, "WX\0\0", 5))
+    abort ();
+  if (memcpy (p + 1, "", 1) != p + 1 || memcmp (p, "A\0CDE", 6))
+    abort ();
+  if (memcpy (p + 3, "FGHI", 4) != p + 3 || memcmp (p, "A\0CFGHI", 8))
+    abort ();
+
+  i = 8;
+  memcpy (p + 20, "qrstu", 6);
+  memcpy (p + 25, "QRSTU", 6);
+  if (memcpy (p + 25 + 1, s1, 3) != p + 25 + 1
+      || memcmp (p + 25, "Q123U", 6))
+    abort ();
+
+  if (memcpy (memcpy (p, "abcdEFG", 4) + 4, "efg", 4) != p + 4
+      || memcmp (p, "abcdefg", 8))
+    abort();
+
+  /* Test at least one instance of the __builtin_ style.  We do this
+     to ensure that it works and that the prototype is correct.  */
+  if (__builtin_memcpy (p, "ABCDE", 6) != p || memcmp (p, "ABCDE", 6))
+    abort ();
+
+  memcpy (p + 5, s3, 1);
+  if (memcmp (p, "ABCDEFg", 8))
+    abort ();
+
+  memcpy_disallowed = 0;
+  if (chk_calls)
+    abort ();
+  chk_calls = 0;
+
+  memcpy (p + 6, s1 + 1, l1);
+  if (memcmp (p, "ABCDEF2", 8))
+    abort ();
+
+  /* The above memcpy copies into an object with known size, but
+     unknown length, so it should be a __memcpy_chk call.  */
+  if (chk_calls != 1)
+    abort ();
+}
+
+long buf1[64];
+char *buf2 = (char *) (buf1 + 32);
+long buf5[20];
+char buf7[20];
+
+void
+__attribute__((noinline))
+test2_sub (long *buf3, char *buf4, char *buf6, int n)
+{
+  int i = 0;
+
+  /* All the memcpy/__builtin_memcpy/__builtin___memcpy_chk
+     calls in this routine are either fixed length, or have
+     side-effects in __builtin_object_size arguments, or
+     dst doesn't point into a known object.  */
+  chk_calls = 0;
+
+  /* These should probably be handled by store_by_pieces on most arches.  */
+  if (memcpy (buf1, "ABCDEFGHI", 9) != (char *) buf1
+      || memcmp (buf1, "ABCDEFGHI\0", 11))
+    abort ();
+
+  if (memcpy (buf1, "abcdefghijklmnopq", 17) != (char *) buf1
+      || memcmp (buf1, "abcdefghijklmnopq\0", 19))
+    abort ();
+
+  if (__builtin_memcpy (buf3, "ABCDEF", 6) != (char *) buf1
+      || memcmp (buf1, "ABCDEFghijklmnopq\0", 19))
+    abort ();
+
+  if (__builtin_memcpy (buf3, "a", 1) != (char *) buf1
+      || memcmp (buf1, "aBCDEFghijklmnopq\0", 19))
+    abort ();
+
+  if (memcpy ((char *) buf3 + 2, "bcd" + ++i, 2) != (char *) buf1 + 2
+      || memcmp (buf1, "aBcdEFghijklmnopq\0", 19)
+      || i != 1)
+    abort ();
+
+  /* These should probably be handled by move_by_pieces on most arches.  */
+  if (memcpy ((char *) buf3 + 4, buf5, 6) != (char *) buf1 + 4
+      || memcmp (buf1, "aBcdRSTUVWklmnopq\0", 19))
+    abort ();
+
+  if (__builtin_memcpy ((char *) buf1 + ++i + 8, (char *) buf5 + 1, 1)
+      != (char *) buf1 + 10
+      || memcmp (buf1, "aBcdRSTUVWSlmnopq\0", 19)
+      || i != 2)
+    abort ();
+
+  if (memcpy ((char *) buf3 + 14, buf6, 2) != (char *) buf1 + 14
+      || memcmp (buf1, "aBcdRSTUVWSlmnrsq\0", 19))
+    abort ();
+
+  if (memcpy (buf3, buf5, 8) != (char *) buf1
+      || memcmp (buf1, "RSTUVWXYVWSlmnrsq\0", 19))
+    abort ();
+
+  if (memcpy (buf3, buf5, 17) != (char *) buf1
+      || memcmp (buf1, "RSTUVWXYZ01234567\0", 19))
+    abort ();
+
+  __builtin_memcpy (buf3, "aBcdEFghijklmnopq\0", 19);
+
+  /* These should be handled either by movmemendM or memcpy
+     call.  */
+
+  /* buf3 points to an unknown object, so __memcpy_chk should not be done.  */
+  if (memcpy ((char *) buf3 + 4, buf5, n + 6) != (char *) buf1 + 4
+      || memcmp (buf1, "aBcdRSTUVWklmnopq\0", 19))
+    abort ();
+
+  /* This call has side-effects in dst, therefore no checking.  */
+  if (__builtin___memcpy_chk ((char *) buf1 + ++i + 8, (char *) buf5 + 1,
+			      n + 1, os ((char *) buf1 + ++i + 8))
+      != (char *) buf1 + 11
+      || memcmp (buf1, "aBcdRSTUVWkSmnopq\0", 19)
+      || i != 3)
+    abort ();
+
+  if (memcpy ((char *) buf3 + 14, buf6, n + 2) != (char *) buf1 + 14
+      || memcmp (buf1, "aBcdRSTUVWkSmnrsq\0", 19))
+    abort ();
+
+  i = 1;
+
+  /* These might be handled by store_by_pieces.  */
+  if (memcpy (buf2, "ABCDEFGHI", 9) != buf2
+      || memcmp (buf2, "ABCDEFGHI\0", 11))
+    abort ();
+
+  if (memcpy (buf2, "abcdefghijklmnopq", 17) != buf2
+      || memcmp (buf2, "abcdefghijklmnopq\0", 19))
+    abort ();
+
+  if (__builtin_memcpy (buf4, "ABCDEF", 6) != buf2
+      || memcmp (buf2, "ABCDEFghijklmnopq\0", 19))
+    abort ();
+
+  if (__builtin_memcpy (buf4, "a", 1) != buf2
+      || memcmp (buf2, "aBCDEFghijklmnopq\0", 19))
+    abort ();
+
+  if (memcpy (buf4 + 2, "bcd" + i++, 2) != buf2 + 2
+      || memcmp (buf2, "aBcdEFghijklmnopq\0", 19)
+      || i != 2)
+    abort ();
+
+  /* These might be handled by move_by_pieces.  */
+  if (memcpy (buf4 + 4, buf7, 6) != buf2 + 4
+      || memcmp (buf2, "aBcdRSTUVWklmnopq\0", 19))
+    abort ();
+
+  /* Side effect.  */
+  if (__builtin___memcpy_chk (buf2 + i++ + 8, buf7 + 1, 1,
+			      os (buf2 + i++ + 8))
+      != buf2 + 10
+      || memcmp (buf2, "aBcdRSTUVWSlmnopq\0", 19)
+      || i != 3)
+    abort ();
+
+  if (memcpy (buf4 + 14, buf6, 2) != buf2 + 14
+      || memcmp (buf2, "aBcdRSTUVWSlmnrsq\0", 19))
+    abort ();
+
+  __builtin_memcpy (buf4, "aBcdEFghijklmnopq\0", 19);
+
+  /* These should be handled either by movmemendM or memcpy
+     call.  */
+  if (memcpy (buf4 + 4, buf7, n + 6) != buf2 + 4
+      || memcmp (buf2, "aBcdRSTUVWklmnopq\0", 19))
+    abort ();
+
+  /* Side effect.  */
+  if (__builtin___memcpy_chk (buf2 + i++ + 8, buf7 + 1, n + 1,
+			      os (buf2 + i++ + 8))
+      != buf2 + 11
+      || memcmp (buf2, "aBcdRSTUVWkSmnopq\0", 19)
+      || i != 4)
+    abort ();
+
+  if (memcpy (buf4 + 14, buf6, n + 2) != buf2 + 14
+      || memcmp (buf2, "aBcdRSTUVWkSmnrsq\0", 19))
+    abort ();
+
+  if (chk_calls)
+    abort ();
+}
+
+void
+__attribute__((noinline))
+test2 (void)
+{
+  long *x;
+  char *y;
+  int z;
+  __builtin_memcpy (buf5, "RSTUVWXYZ0123456789", 20);
+  __builtin_memcpy (buf7, "RSTUVWXYZ0123456789", 20);
+ __asm ("" : "=r" (x) : "0" (buf1));
+ __asm ("" : "=r" (y) : "0" (buf2));
+ __asm ("" : "=r" (z) : "0" (0));
+  test2_sub (x, y, "rstuvwxyz", z);
+}
+
+/* Test whether compile time checking is done where it should
+   and so is runtime object size checking.  */
+void
+__attribute__((noinline))
+test3 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4];
+  char buf3[20];
+  int i;
+  size_t l;
+
+  /* The following calls should do runtime checking
+     - length is not known, but destination is.  */
+  chk_calls = 0;
+  memcpy (a.buf1 + 2, s3, l1);
+  memcpy (r, s3, l1 + 1);
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  memcpy (r, s2, l1 + 2);
+  memcpy (r + 2, s3, l1);
+  r = buf3;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1];
+      else if (i == l1)
+	r = &a.buf2[7];
+      else if (i == l1 + 1)
+	r = &buf3[5];
+      else if (i == l1 + 2)
+	r = &a.buf1[9];
+    }
+  memcpy (r, s2, l1);
+  if (chk_calls != 5)
+    abort ();
+
+  /* Following have known destination and known length,
+     so if optimizing certainly shouldn't result in the checking
+     variants.  */
+  chk_calls = 0;
+  memcpy (a.buf1 + 2, s3, 1);
+  memcpy (r, s3, 2);
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  memcpy (r, s2, 3);
+  r = buf3;
+  l = 4;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1], l = 2;
+      else if (i == l1)
+	r = &a.buf2[7], l = 3;
+      else if (i == l1 + 1)
+	r = &buf3[5], l = 4;
+      else if (i == l1 + 2)
+	r = &a.buf1[9], l = 1;
+    }
+  memcpy (r, s2, 1);
+  /* Here, l is known to be at most 4 and __builtin_object_size (&buf3[16], 0)
+     is 4, so this doesn't need runtime checking.  */
+  memcpy (&buf3[16], s2, l);
+  if (chk_calls)
+    abort ();
+  chk_calls = 0;
+}
+
+/* Test whether runtime and/or compile time checking catches
+   buffer overflows.  */
+void
+__attribute__((noinline))
+test4 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char buf3[20];
+
+  chk_fail_allowed = 1;
+  /* Runtime checks.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      memcpy (&a.buf2[9], s2, l1 + 1);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      memcpy (&a.buf2[7], s3, strlen (s3) + 1);
+      abort ();
+    }
+  /* This should be detectable at compile time already.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      memcpy (&buf3[19], "ab", 2);
+      abort ();
+    }
+  chk_fail_allowed = 0;
+}
+
+#ifndef MAX_OFFSET
+#define MAX_OFFSET (sizeof (long long))
+#endif
+
+#ifndef MAX_COPY
+#define MAX_COPY (10 * sizeof (long long))
+#endif
+
+#ifndef MAX_EXTRA
+#define MAX_EXTRA (sizeof (long long))
+#endif
+
+#define MAX_LENGTH (MAX_OFFSET + MAX_COPY + MAX_EXTRA)
+
+/* Use a sequence length that is not divisible by two, to make it more
+   likely to detect when words are mixed up.  */
+#define SEQUENCE_LENGTH 31
+
+static union {
+  char buf[MAX_LENGTH];
+  long long align_int;
+  long double align_fp;
+} u1, u2;
+
+void
+__attribute__((noinline))
+test5 (void)
+{
+  int off1, off2, len, i;
+  char *p, *q, c;
+
+  for (off1 = 0; off1 < MAX_OFFSET; off1++)
+    for (off2 = 0; off2 < MAX_OFFSET; off2++)
+      for (len = 1; len < MAX_COPY; len++)
+	{
+	  for (i = 0, c = 'A'; i < MAX_LENGTH; i++, c++)
+	    {
+	      u1.buf[i] = 'a';
+	      if (c >= 'A' + SEQUENCE_LENGTH)
+		c = 'A';
+	      u2.buf[i] = c;
+	    }
+
+	  p = memcpy (u1.buf + off1, u2.buf + off2, len);
+	  if (p != u1.buf + off1)
+	    abort ();
+
+	  q = u1.buf;
+	  for (i = 0; i < off1; i++, q++)
+	    if (*q != 'a')
+	      abort ();
+
+	  for (i = 0, c = 'A' + off2; i < len; i++, q++, c++)
+	    {
+	      if (c >= 'A' + SEQUENCE_LENGTH)
+		c = 'A';
+	      if (*q != c)
+		abort ();
+	    }
+
+	  for (i = 0; i < MAX_EXTRA; i++, q++)
+	    if (*q != 'a')
+	      abort ();
+	}
+}
+
+#define TESTSIZE 80
+
+char srcb[TESTSIZE] __attribute__ ((aligned));
+char dstb[TESTSIZE] __attribute__ ((aligned));
+
+void
+__attribute__((noinline))
+check (char *test, char *match, int n)
+{
+  if (memcmp (test, match, n))
+    abort ();
+}
+
+#define TN(n) \
+{ memset (dstb, 0, n); memcpy (dstb, srcb, n); check (dstb, srcb, n); }
+#define T(n) \
+TN (n) \
+TN ((n) + 1) \
+TN ((n) + 2) \
+TN ((n) + 3)
+
+void
+__attribute__((noinline))
+test6 (void)
+{
+  int i;
+
+  chk_calls = 0;
+
+  for (i = 0; i < sizeof (srcb); ++i)
+      srcb[i] = 'a' + i % 26;
+
+  T (0);
+  T (4);
+  T (8);
+  T (12);
+  T (16);
+  T (20);
+  T (24);
+  T (28);
+  T (32);
+  T (36);
+  T (40);
+  T (44);
+  T (48);
+  T (52);
+  T (56);
+  T (60);
+  T (64);
+  T (68);
+  T (72);
+  T (76);
+
+  /* All memcpy calls in this routine have constant arguments.  */
+  if (chk_calls)
+    abort ();
+}
+
+void
+main_test (void)
+{
+#ifndef __OPTIMIZE__
+  /* Object size checking is only intended for -O[s123].  */
+  return;
+#endif
+  __asm ("" : "=r" (l1) : "0" (l1));
+  test1 ();
+  test2 ();
+  test3 ();
+  test4 ();
+  test5 ();
+  test6 ();
+}
--- gcc/testsuite/gcc.c-torture/execute/builtins/memset-chk.c.jj	2004-09-20 18:09:25.258570698 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/memset-chk.c	2004-09-20 18:09:25.258570698 +0200
@@ -0,0 +1,721 @@
+/* Copyright (C) 2004  Free Software Foundation.
+
+   Ensure builtin __memset_chk performs correctly.  */
+
+extern void abort (void);
+typedef __SIZE_TYPE__ size_t;
+extern size_t strlen(const char *);
+extern void *memcpy (void *, const void *, size_t);
+extern void *memset (void *, int, size_t);
+extern int memcmp (const void *, const void *, size_t);
+
+#include "chk.h"
+
+char buffer[32];
+int argc = 1;
+size_t l1 = 1;
+char *s3 = "FGH";
+char *s4;
+
+void
+__attribute__((noinline))
+test1 (void)
+{
+  memset_disallowed = 1;
+  chk_calls = 0;
+  memset (buffer, argc, 0);
+  memset (buffer, argc, 1);
+  memset (buffer, argc, 2);
+  memset (buffer, argc, 3);
+  memset (buffer, argc, 4);
+  memset (buffer, argc, 5);
+  memset (buffer, argc, 6);
+  memset (buffer, argc, 7);
+  memset (buffer, argc, 8);
+  memset (buffer, argc, 9);
+  memset (buffer, argc, 10);
+  memset (buffer, argc, 11);
+  memset (buffer, argc, 12);
+  memset (buffer, argc, 13);
+  memset (buffer, argc, 14);
+  memset (buffer, argc, 15);
+  memset (buffer, argc, 16);
+  memset (buffer, argc, 17);
+  memset_disallowed = 0;
+  if (chk_calls)
+    abort ();
+}
+
+/* Test whether compile time checking is done where it should
+   and so is runtime object size checking.  */
+void
+__attribute__((noinline))
+test2 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4];
+  char buf3[20];
+  int i;
+  size_t l;
+
+  /* The following calls should do runtime checking
+     - length is not known, but destination is.  */
+  chk_calls = 0;
+  memset (a.buf1 + 2, 'a', l1);
+  memset (r, '\0', l1 + 1);
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  memset (r, argc, l1 + 2);
+  memset (r + 2, 'Q', l1);
+  r = buf3;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1];
+      else if (i == l1)
+	r = &a.buf2[7];
+      else if (i == l1 + 1)
+	r = &buf3[5];
+      else if (i == l1 + 2)
+	r = &a.buf1[9];
+    }
+  memset (r, '\0', l1);
+  if (chk_calls != 5)
+    abort ();
+
+  /* Following have known destination and known length,
+     so if optimizing certainly shouldn't result in the checking
+     variants.  */
+  chk_calls = 0;
+  memset (a.buf1 + 2, '\0', 1);
+  memset (r, argc, 2);
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  memset (r, 'N', 3);
+  r = buf3;
+  l = 4;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1], l = 2;
+      else if (i == l1)
+	r = &a.buf2[7], l = 3;
+      else if (i == l1 + 1)
+	r = &buf3[5], l = 4;
+      else if (i == l1 + 2)
+	r = &a.buf1[9], l = 1;
+    }
+  memset (r, 'H', 1);
+  /* Here, l is known to be at most 4 and __builtin_object_size (&buf3[16], 0)
+     is 4, so this doesn't need runtime checking.  */
+  memset (&buf3[16], 'd', l);
+  /* Neither length nor destination known.  Doesn't need runtime checking.  */
+  memset (s4, 'a', l1);
+  memset (s4 + 2, '\0', l1 + 2);
+  /* Destination unknown.  */
+  memset (s4 + 4, 'b', 2);
+  memset (s4 + 6, '\0', 4);
+  if (chk_calls)
+    abort ();
+  chk_calls = 0;
+}
+
+/* Test whether runtime and/or compile time checking catches
+   buffer overflows.  */
+void
+__attribute__((noinline))
+test3 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char buf3[20];
+
+  chk_fail_allowed = 1;
+  /* Runtime checks.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      memset (&a.buf2[9], '\0', l1 + 1);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      memset (&a.buf2[7], 'T', strlen (s3) + 1);
+      abort ();
+    }
+  /* This should be detectable at compile time already.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      memset (&buf3[19], 'b', 2);
+      abort ();
+    }
+  chk_fail_allowed = 0;
+}
+
+#ifndef MAX_OFFSET
+#define MAX_OFFSET (sizeof (long long))
+#endif
+
+#ifndef MAX_COPY
+#define MAX_COPY (10 * sizeof (long long))
+#define MAX_COPY2 15
+#else
+#define MAX_COPY2 MAX_COPY
+#endif
+
+#ifndef MAX_EXTRA
+#define MAX_EXTRA (sizeof (long long))
+#endif
+
+#define MAX_LENGTH (MAX_OFFSET + MAX_COPY + MAX_EXTRA)
+#define MAX_LENGTH2 (MAX_OFFSET + MAX_COPY2 + MAX_EXTRA)
+
+static union {
+  char buf[MAX_LENGTH];
+  long long align_int;
+  long double align_fp;
+} u;
+
+char A = 'A';
+
+void
+__attribute__((noinline))
+test4 (void)
+{
+  int off, len, i;
+  char *p, *q;
+
+  for (off = 0; off < MAX_OFFSET; off++)
+    for (len = 1; len < MAX_COPY; len++)
+      {
+	for (i = 0; i < MAX_LENGTH; i++)
+	  u.buf[i] = 'a';
+
+	p = memset (u.buf + off, '\0', len);
+	if (p != u.buf + off)
+	  abort ();
+
+	q = u.buf;
+	for (i = 0; i < off; i++, q++)
+	  if (*q != 'a')
+	    abort ();
+
+	for (i = 0; i < len; i++, q++)
+	  if (*q != '\0')
+	    abort ();
+
+	for (i = 0; i < MAX_EXTRA; i++, q++)
+	  if (*q != 'a')
+	    abort ();
+
+	p = memset (u.buf + off, A, len);
+	if (p != u.buf + off)
+	  abort ();
+
+	q = u.buf;
+	for (i = 0; i < off; i++, q++)
+	  if (*q != 'a')
+	    abort ();
+
+	for (i = 0; i < len; i++, q++)
+	  if (*q != 'A')
+	    abort ();
+
+	for (i = 0; i < MAX_EXTRA; i++, q++)
+	  if (*q != 'a')
+	    abort ();
+
+	p = memset (u.buf + off, 'B', len);
+	if (p != u.buf + off)
+	  abort ();
+
+	q = u.buf;
+	for (i = 0; i < off; i++, q++)
+	  if (*q != 'a')
+	    abort ();
+
+	for (i = 0; i < len; i++, q++)
+	  if (*q != 'B')
+	    abort ();
+
+	for (i = 0; i < MAX_EXTRA; i++, q++)
+	  if (*q != 'a')
+	    abort ();
+      }
+}
+
+static union {
+  char buf[MAX_LENGTH2];
+  long long align_int;
+  long double align_fp;
+} u2;
+
+void reset ()
+{
+  int i;
+
+  for (i = 0; i < MAX_LENGTH2; i++)
+    u2.buf[i] = 'a';
+}
+
+void check (int off, int len, int ch)
+{
+  char *q;
+  int i;
+
+  q = u2.buf;
+  for (i = 0; i < off; i++, q++)
+    if (*q != 'a')
+      abort ();
+
+  for (i = 0; i < len; i++, q++)
+    if (*q != ch)
+      abort ();
+
+  for (i = 0; i < MAX_EXTRA; i++, q++)
+    if (*q != 'a')
+      abort ();
+}
+
+void
+__attribute__((noinline))
+test5 (void)
+{
+  int off;
+  char *p;
+
+  /* len == 1 */
+  for (off = 0; off < MAX_OFFSET; off++)
+    {
+      reset ();
+
+      p = memset (u2.buf + off, '\0', 1);
+      if (p != u2.buf + off) abort ();
+      check (off, 1, '\0');
+
+      p = memset (u2.buf + off, A, 1);
+      if (p != u2.buf + off) abort ();
+      check (off, 1, 'A');
+
+      p = memset (u2.buf + off, 'B', 1);
+      if (p != u2.buf + off) abort ();
+      check (off, 1, 'B');
+    }
+
+  /* len == 2 */
+  for (off = 0; off < MAX_OFFSET; off++)
+    {
+      reset ();
+
+      p = memset (u2.buf + off, '\0', 2);
+      if (p != u2.buf + off) abort ();
+      check (off, 2, '\0');
+
+      p = memset (u2.buf + off, A, 2);
+      if (p != u2.buf + off) abort ();
+      check (off, 2, 'A');
+
+      p = memset (u2.buf + off, 'B', 2);
+      if (p != u2.buf + off) abort ();
+      check (off, 2, 'B');
+    }
+
+  /* len == 3 */
+  for (off = 0; off < MAX_OFFSET; off++)
+    {
+      reset ();
+
+      p = memset (u2.buf + off, '\0', 3);
+      if (p != u2.buf + off) abort ();
+      check (off, 3, '\0');
+
+      p = memset (u2.buf + off, A, 3);
+      if (p != u2.buf + off) abort ();
+      check (off, 3, 'A');
+
+      p = memset (u2.buf + off, 'B', 3);
+      if (p != u2.buf + off) abort ();
+      check (off, 3, 'B');
+    }
+
+  /* len == 4 */
+  for (off = 0; off < MAX_OFFSET; off++)
+    {
+      reset ();
+
+      p = memset (u2.buf + off, '\0', 4);
+      if (p != u2.buf + off) abort ();
+      check (off, 4, '\0');
+
+      p = memset (u2.buf + off, A, 4);
+      if (p != u2.buf + off) abort ();
+      check (off, 4, 'A');
+
+      p = memset (u2.buf + off, 'B', 4);
+      if (p != u2.buf + off) abort ();
+      check (off, 4, 'B');
+    }
+
+  /* len == 5 */
+  for (off = 0; off < MAX_OFFSET; off++)
+    {
+      reset ();
+
+      p = memset (u2.buf + off, '\0', 5);
+      if (p != u2.buf + off) abort ();
+      check (off, 5, '\0');
+
+      p = memset (u2.buf + off, A, 5);
+      if (p != u2.buf + off) abort ();
+      check (off, 5, 'A');
+
+      p = memset (u2.buf + off, 'B', 5);
+      if (p != u2.buf + off) abort ();
+      check (off, 5, 'B');
+    }
+
+  /* len == 6 */
+  for (off = 0; off < MAX_OFFSET; off++)
+    {
+      reset ();
+
+      p = memset (u2.buf + off, '\0', 6);
+      if (p != u2.buf + off) abort ();
+      check (off, 6, '\0');
+
+      p = memset (u2.buf + off, A, 6);
+      if (p != u2.buf + off) abort ();
+      check (off, 6, 'A');
+
+      p = memset (u2.buf + off, 'B', 6);
+      if (p != u2.buf + off) abort ();
+      check (off, 6, 'B');
+    }
+
+  /* len == 7 */
+  for (off = 0; off < MAX_OFFSET; off++)
+    {
+      reset ();
+
+      p = memset (u2.buf + off, '\0', 7);
+      if (p != u2.buf + off) abort ();
+      check (off, 7, '\0');
+
+      p = memset (u2.buf + off, A, 7);
+      if (p != u2.buf + off) abort ();
+      check (off, 7, 'A');
+
+      p = memset (u2.buf + off, 'B', 7);
+      if (p != u2.buf + off) abort ();
+      check (off, 7, 'B');
+    }
+
+  /* len == 8 */
+  for (off = 0; off < MAX_OFFSET; off++)
+    {
+      reset ();
+
+      p = memset (u2.buf + off, '\0', 8);
+      if (p != u2.buf + off) abort ();
+      check (off, 8, '\0');
+
+      p = memset (u2.buf + off, A, 8);
+      if (p != u2.buf + off) abort ();
+      check (off, 8, 'A');
+
+      p = memset (u2.buf + off, 'B', 8);
+      if (p != u2.buf + off) abort ();
+      check (off, 8, 'B');
+    }
+
+  /* len == 9 */
+  for (off = 0; off < MAX_OFFSET; off++)
+    {
+      reset ();
+
+      p = memset (u2.buf + off, '\0', 9);
+      if (p != u2.buf + off) abort ();
+      check (off, 9, '\0');
+
+      p = memset (u2.buf + off, A, 9);
+      if (p != u2.buf + off) abort ();
+      check (off, 9, 'A');
+
+      p = memset (u2.buf + off, 'B', 9);
+      if (p != u2.buf + off) abort ();
+      check (off, 9, 'B');
+    }
+
+  /* len == 10 */
+  for (off = 0; off < MAX_OFFSET; off++)
+    {
+      reset ();
+
+      p = memset (u2.buf + off, '\0', 10);
+      if (p != u2.buf + off) abort ();
+      check (off, 10, '\0');
+
+      p = memset (u2.buf + off, A, 10);
+      if (p != u2.buf + off) abort ();
+      check (off, 10, 'A');
+
+      p = memset (u2.buf + off, 'B', 10);
+      if (p != u2.buf + off) abort ();
+      check (off, 10, 'B');
+    }
+
+  /* len == 11 */
+  for (off = 0; off < MAX_OFFSET; off++)
+    {
+      reset ();
+
+      p = memset (u2.buf + off, '\0', 11);
+      if (p != u2.buf + off) abort ();
+      check (off, 11, '\0');
+
+      p = memset (u2.buf + off, A, 11);
+      if (p != u2.buf + off) abort ();
+      check (off, 11, 'A');
+
+      p = memset (u2.buf + off, 'B', 11);
+      if (p != u2.buf + off) abort ();
+      check (off, 11, 'B');
+    }
+
+  /* len == 12 */
+  for (off = 0; off < MAX_OFFSET; off++)
+    {
+      reset ();
+
+      p = memset (u2.buf + off, '\0', 12);
+      if (p != u2.buf + off) abort ();
+      check (off, 12, '\0');
+
+      p = memset (u2.buf + off, A, 12);
+      if (p != u2.buf + off) abort ();
+      check (off, 12, 'A');
+
+      p = memset (u2.buf + off, 'B', 12);
+      if (p != u2.buf + off) abort ();
+      check (off, 12, 'B');
+    }
+
+  /* len == 13 */
+  for (off = 0; off < MAX_OFFSET; off++)
+    {
+      reset ();
+
+      p = memset (u2.buf + off, '\0', 13);
+      if (p != u2.buf + off) abort ();
+      check (off, 13, '\0');
+
+      p = memset (u2.buf + off, A, 13);
+      if (p != u2.buf + off) abort ();
+      check (off, 13, 'A');
+
+      p = memset (u2.buf + off, 'B', 13);
+      if (p != u2.buf + off) abort ();
+      check (off, 13, 'B');
+    }
+
+  /* len == 14 */
+  for (off = 0; off < MAX_OFFSET; off++)
+    {
+      reset ();
+
+      p = memset (u2.buf + off, '\0', 14);
+      if (p != u2.buf + off) abort ();
+      check (off, 14, '\0');
+
+      p = memset (u2.buf + off, A, 14);
+      if (p != u2.buf + off) abort ();
+      check (off, 14, 'A');
+
+      p = memset (u2.buf + off, 'B', 14);
+      if (p != u2.buf + off) abort ();
+      check (off, 14, 'B');
+    }
+
+  /* len == 15 */
+  for (off = 0; off < MAX_OFFSET; off++)
+    {
+      reset ();
+
+      p = memset (u2.buf + off, '\0', 15);
+      if (p != u2.buf + off) abort ();
+      check (off, 15, '\0');
+
+      p = memset (u2.buf + off, A, 15);
+      if (p != u2.buf + off) abort ();
+      check (off, 15, 'A');
+
+      p = memset (u2.buf + off, 'B', 15);
+      if (p != u2.buf + off) abort ();
+      check (off, 15, 'B');
+    }
+}
+
+void
+__attribute__((noinline))
+test6 (void)
+{
+  int len;
+  char *p;
+
+  /* off == 0 */
+  for (len = 0; len < MAX_COPY2; len++)
+    {
+      reset ();
+
+      p = memset (u2.buf, '\0', len);
+      if (p != u2.buf) abort ();
+      check (0, len, '\0');
+
+      p = memset (u2.buf, A, len);
+      if (p != u2.buf) abort ();
+      check (0, len, 'A');
+
+      p = memset (u2.buf, 'B', len);
+      if (p != u2.buf) abort ();
+      check (0, len, 'B');
+    }
+
+  /* off == 1 */
+  for (len = 0; len < MAX_COPY2; len++)
+    {
+      reset ();
+
+      p = memset (u2.buf+1, '\0', len);
+      if (p != u2.buf+1) abort ();
+      check (1, len, '\0');
+
+      p = memset (u2.buf+1, A, len);
+      if (p != u2.buf+1) abort ();
+      check (1, len, 'A');
+
+      p = memset (u2.buf+1, 'B', len);
+      if (p != u2.buf+1) abort ();
+      check (1, len, 'B');
+    }
+
+  /* off == 2 */
+  for (len = 0; len < MAX_COPY2; len++)
+    {
+      reset ();
+
+      p = memset (u2.buf+2, '\0', len);
+      if (p != u2.buf+2) abort ();
+      check (2, len, '\0');
+
+      p = memset (u2.buf+2, A, len);
+      if (p != u2.buf+2) abort ();
+      check (2, len, 'A');
+
+      p = memset (u2.buf+2, 'B', len);
+      if (p != u2.buf+2) abort ();
+      check (2, len, 'B');
+    }
+
+  /* off == 3 */
+  for (len = 0; len < MAX_COPY2; len++)
+    {
+      reset ();
+
+      p = memset (u2.buf+3, '\0', len);
+      if (p != u2.buf+3) abort ();
+      check (3, len, '\0');
+
+      p = memset (u2.buf+3, A, len);
+      if (p != u2.buf+3) abort ();
+      check (3, len, 'A');
+
+      p = memset (u2.buf+3, 'B', len);
+      if (p != u2.buf+3) abort ();
+      check (3, len, 'B');
+    }
+
+  /* off == 4 */
+  for (len = 0; len < MAX_COPY2; len++)
+    {
+      reset ();
+
+      p = memset (u2.buf+4, '\0', len);
+      if (p != u2.buf+4) abort ();
+      check (4, len, '\0');
+
+      p = memset (u2.buf+4, A, len);
+      if (p != u2.buf+4) abort ();
+      check (4, len, 'A');
+
+      p = memset (u2.buf+4, 'B', len);
+      if (p != u2.buf+4) abort ();
+      check (4, len, 'B');
+    }
+
+  /* off == 5 */
+  for (len = 0; len < MAX_COPY2; len++)
+    {
+      reset ();
+
+      p = memset (u2.buf+5, '\0', len);
+      if (p != u2.buf+5) abort ();
+      check (5, len, '\0');
+
+      p = memset (u2.buf+5, A, len);
+      if (p != u2.buf+5) abort ();
+      check (5, len, 'A');
+
+      p = memset (u2.buf+5, 'B', len);
+      if (p != u2.buf+5) abort ();
+      check (5, len, 'B');
+    }
+
+  /* off == 6 */
+  for (len = 0; len < MAX_COPY2; len++)
+    {
+      reset ();
+
+      p = memset (u2.buf+6, '\0', len);
+      if (p != u2.buf+6) abort ();
+      check (6, len, '\0');
+
+      p = memset (u2.buf+6, A, len);
+      if (p != u2.buf+6) abort ();
+      check (6, len, 'A');
+
+      p = memset (u2.buf+6, 'B', len);
+      if (p != u2.buf+6) abort ();
+      check (6, len, 'B');
+    }
+
+  /* off == 7 */
+  for (len = 0; len < MAX_COPY2; len++)
+    {
+      reset ();
+
+      p = memset (u2.buf+7, '\0', len);
+      if (p != u2.buf+7) abort ();
+      check (7, len, '\0');
+
+      p = memset (u2.buf+7, A, len);
+      if (p != u2.buf+7) abort ();
+      check (7, len, 'A');
+
+      p = memset (u2.buf+7, 'B', len);
+      if (p != u2.buf+7) abort ();
+      check (7, len, 'B');
+    }
+}
+
+void
+main_test (void)
+{
+#ifndef __OPTIMIZE__
+  /* Object size checking is only intended for -O[s123].  */
+  return;
+#endif
+  __asm ("" : "=r" (l1) : "0" (l1));
+  s4 = buffer;
+  test1 ();
+  test2 ();
+  test3 ();
+  test4 ();
+  test5 ();
+  test6 ();
+}
--- gcc/testsuite/gcc.c-torture/execute/builtins/snprintf-chk.c.jj	2004-09-20 18:09:25.280566804 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/snprintf-chk.c	2004-09-20 18:09:25.280566804 +0200
@@ -0,0 +1,220 @@
+/* Copyright (C) 2004  Free Software Foundation.
+
+   Ensure builtin __snprintf_chk performs correctly.  */
+
+extern void abort (void);
+typedef __SIZE_TYPE__ size_t;
+extern size_t strlen(const char *);
+extern void *memcpy (void *, const void *, size_t);
+extern char *strcpy (char *, const char *);
+extern int memcmp (const void *, const void *, size_t);
+extern void *memset (void *, int, size_t);
+extern int sprintf (char *, const char *, ...);
+extern int snprintf (char *, size_t, const char *, ...);
+
+#include "chk.h"
+
+const char s1[] = "123";
+char p[32] = "";
+char *s2 = "defg";
+char *s3 = "FGH";
+char *s4;
+size_t l1 = 1;
+static char buffer[32];
+char *ptr = "barf";
+
+void
+__attribute__((noinline))
+test1 (void)
+{
+  chk_calls = 0;
+  /* snprintf_disallowed = 1; */
+
+  memset (buffer, 'A', 32);
+  snprintf (buffer, 4, "foo");
+  if (memcmp (buffer, "foo", 4) || buffer[4] != 'A')
+    abort ();
+
+  memset (buffer, 'A', 32);
+  if (snprintf (buffer, 4, "foo bar") != 7)
+    abort ();
+  if (memcmp (buffer, "foo", 4) || buffer[4] != 'A')
+    abort ();
+
+  memset (buffer, 'A', 32);
+  snprintf (buffer, 32, "%s", "bar");
+  if (memcmp (buffer, "bar", 4) || buffer[4] != 'A')
+    abort ();
+
+  memset (buffer, 'A', 32);
+  if (snprintf (buffer, 21, "%s", "bar") != 3)
+    abort ();
+  if (memcmp (buffer, "bar", 4) || buffer[4] != 'A')
+    abort ();
+
+  snprintf_disallowed = 0;
+
+  memset (buffer, 'A', 32);
+  if (snprintf (buffer, 4, "%d%d%d", (int) l1, (int) l1 + 1, (int) l1 + 12)
+      != 4)
+    abort ();
+  if (memcmp (buffer, "121", 4) || buffer[4] != 'A')
+    abort ();
+
+  memset (buffer, 'A', 32);
+  if (snprintf (buffer, 32, "%d%d%d", (int) l1, (int) l1 + 1, (int) l1 + 12)
+      != 4)
+    abort ();
+  if (memcmp (buffer, "1213", 5) || buffer[5] != 'A')
+    abort ();
+
+  if (chk_calls)
+    abort ();
+
+  memset (buffer, 'A', 32);
+  snprintf (buffer, strlen (ptr) + 1, "%s", ptr);
+  if (memcmp (buffer, "barf", 5) || buffer[5] != 'A')
+    abort ();
+
+  memset (buffer, 'A', 32);
+  snprintf (buffer, l1 + 31, "%d - %c", (int) l1 + 27, *ptr);
+  if (memcmp (buffer, "28 - b\0AAAAA", 12))
+    abort ();
+
+  if (chk_calls != 2)
+    abort ();
+  chk_calls = 0;
+
+  memset (s4, 'A', 32);
+  snprintf (s4, l1 + 6, "%d - %c", (int) l1 - 17, ptr[1]);
+  if (memcmp (s4, "-16 - \0AAA", 10))
+    abort ();
+  if (chk_calls)
+    abort ();
+}
+
+/* Test whether compile time checking is done where it should
+   and so is runtime object size checking.  */
+void
+__attribute__((noinline))
+test2 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4];
+  char buf3[20];
+  int i;
+
+  /* The following calls should do runtime checking
+     - length is not known, but destination is.  */
+  chk_calls = 0;
+  snprintf (a.buf1 + 2, l1, "%s", s3 + 3);
+  snprintf (r, l1 + 4, "%s%c", s3 + 4, s3[3]);
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  snprintf (r, strlen (s2) - 2, "%c %s", s2[2], s2 + 4);
+  snprintf (r + 2, l1, s3 + 3);
+  r = buf3;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1];
+      else if (i == l1)
+	r = &a.buf2[7];
+      else if (i == l1 + 1)
+	r = &buf3[5];
+      else if (i == l1 + 2)
+	r = &a.buf1[9];
+    }
+  snprintf (r, l1, s2 + 4);
+  if (chk_calls != 5)
+    abort ();
+
+  /* Following have known destination and known source length,
+     so if optimizing certainly shouldn't result in the checking
+     variants.  */
+  chk_calls = 0;
+  /* snprintf_disallowed = 1; */
+  snprintf (a.buf1 + 2, 4, "");
+  snprintf (r, 1, "a");
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  snprintf (r, 3, "%s", s1 + 1);
+  r = buf3;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1];
+      else if (i == l1)
+	r = &a.buf2[7];
+      else if (i == l1 + 1)
+	r = &buf3[5];
+      else if (i == l1 + 2)
+	r = &a.buf1[9];
+    }
+  snprintf (r, 1, "%s", "");
+  snprintf (r, 0, "%s", "");
+  snprintf_disallowed = 0;
+  /* Unknown destination and source, no checking.  */
+  snprintf (s4, l1 + 31, "%s %d", s3, 0);
+  if (chk_calls)
+    abort ();
+}
+
+/* Test whether runtime and/or compile time checking catches
+   buffer overflows.  */
+void
+__attribute__((noinline))
+test3 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char buf3[20];
+
+  chk_fail_allowed = 1;
+  /* Runtime checks.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      snprintf (&a.buf2[9], l1 + 1, "%c%s", s2[3], s2 + 4);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      snprintf (&a.buf2[7], l1 + 30, "%s%c", s3 + strlen (s3) - 2, *s3);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      snprintf (&a.buf2[7], l1 + 3, "%d", (int) l1 + 9999);
+      abort ();
+    }
+  /* This should be detectable at compile time already.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      snprintf (&buf3[19], 2, "a");
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      snprintf (&buf3[17], 4, "a");
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      snprintf (&buf3[17], 4, "%s", "abc");
+      abort ();
+    }
+  chk_fail_allowed = 0;
+}
+
+void
+main_test (void)
+{
+#ifndef __OPTIMIZE__
+  /* Object size checking is only intended for -O[s123].  */
+  return;
+#endif
+  __asm ("" : "=r" (s2) : "0" (s2));
+  __asm ("" : "=r" (s3) : "0" (s3));
+  __asm ("" : "=r" (l1) : "0" (l1));
+  s4 = p;
+  test1 ();
+  test2 ();
+  test3 ();
+}
--- gcc/testsuite/gcc.c-torture/execute/builtins/strcat-chk-lib.c.jj	2004-09-20 18:09:25.250572114 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/strcat-chk-lib.c	2004-09-20 18:09:25.251571937 +0200
@@ -0,0 +1 @@
+#include "lib/chk.c"
--- gcc/testsuite/gcc.c-torture/execute/builtins/strncpy-chk-lib.c.jj	2004-09-20 18:09:25.228576008 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/strncpy-chk-lib.c	2004-09-20 18:09:25.228576008 +0200
@@ -0,0 +1 @@
+#include "lib/chk.c"
--- gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk.c.jj	2004-09-20 18:09:25.267569105 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk.c	2004-09-20 18:09:25.267569105 +0200
@@ -0,0 +1,229 @@
+/* Copyright (C) 2004  Free Software Foundation.
+
+   Ensure builtin __strncat_chk performs correctly.  */
+
+extern void abort (void);
+typedef __SIZE_TYPE__ size_t;
+extern size_t strlen (const char *);
+extern void *memcpy (void *, const void *, size_t);
+extern char *strcat (char *, const char *);
+extern char *strncat (char *, const char *, size_t);
+extern int memcmp (const void *, const void *, size_t);
+extern char *strcpy (char *, const char *);
+extern int strcmp (const char *, const char *);
+extern void *memset (void *, int, size_t);
+
+#include "chk.h"
+
+const char s1[] = "123";
+char p[32] = "";
+char *s2 = "defg";
+char *s3 = "FGH";
+char *s4;
+size_t l1 = 1;
+char *s5;
+int x = 123;
+
+void
+__attribute__((noinline))
+test1 (void)
+{
+  const char *const s1 = "hello world";
+  const char *const s2 = "";
+  const char *s3;
+  char dst[64], *d2;
+
+  /* Following strncat calls should be all optimized out.  */
+  chk_calls = 0;
+  strncat_disallowed = 1;
+  strcat_disallowed = 1;
+  strcpy (dst, s1);
+  if (strncat (dst, "", 100) != dst || strcmp (dst, s1))
+    abort ();
+  strcpy (dst, s1);
+  if (strncat (dst, s2, 100) != dst || strcmp (dst, s1))
+    abort ();
+  strcpy (dst, s1); d2 = dst;
+  if (strncat (++d2, s2, 100) != dst+1 || d2 != dst+1 || strcmp (dst, s1))
+    abort ();
+  strcpy (dst, s1); d2 = dst;
+  if (strncat (++d2+5, s2, 100) != dst+6 || d2 != dst+1 || strcmp (dst, s1))
+    abort ();
+  strcpy (dst, s1); d2 = dst;
+  if (strncat (++d2+5, s1+11, 100) != dst+6 || d2 != dst+1 || strcmp (dst, s1))
+    abort ();
+  strcpy (dst, s1); d2 = dst;
+  if (strncat (++d2+5, s1, 0) != dst+6 || d2 != dst+1 || strcmp (dst, s1))
+    abort ();
+  strcpy (dst, s1); d2 = dst; s3 = s1;
+  if (strncat (++d2+5, ++s3, 0) != dst+6 || d2 != dst+1 || strcmp (dst, s1)
+      || s3 != s1 + 1)
+    abort ();
+  strcpy (dst, s1); d2 = dst;
+  if (strncat (++d2+5, "", ++x) != dst+6 || d2 != dst+1 || x != 124
+      || strcmp (dst, s1))
+    abort ();
+  if (chk_calls)
+    abort ();
+  strcat_disallowed = 0;
+
+  /* These __strncat_chk calls should be optimized into __strcat_chk,
+     as strlen (src) <= len.  */
+  strcpy (dst, s1);
+  if (strncat (dst, "foo", 3) != dst || strcmp (dst, "hello worldfoo"))
+    abort ();
+  strcpy (dst, s1);
+  if (strncat (dst, "foo", 100) != dst || strcmp (dst, "hello worldfoo"))
+    abort ();
+  strcpy (dst, s1);
+  if (strncat (dst, s1, 100) != dst || strcmp (dst, "hello worldhello world"))
+    abort ();
+  if (chk_calls != 3)
+    abort ();
+
+  chk_calls = 0;
+  /* The following calls have side-effects in dest, so are not checked.  */
+  strcpy (dst, s1); d2 = dst;
+  if (__builtin___strncat_chk (++d2, s1, 100, os (++d2)) != dst+1
+      || d2 != dst+1 || strcmp (dst, "hello worldhello world"))
+    abort ();
+  strcpy (dst, s1); d2 = dst;
+  if (__builtin___strncat_chk (++d2+5, s1, 100, os (++d2+5)) != dst+6
+      || d2 != dst+1 || strcmp (dst, "hello worldhello world"))
+    abort ();
+  strcpy (dst, s1); d2 = dst;
+  if (__builtin___strncat_chk (++d2+5, s1+5, 100, os (++d2+5)) != dst+6
+      || d2 != dst+1 || strcmp (dst, "hello world world"))
+    abort ();
+  if (chk_calls)
+    abort ();
+
+  chk_calls = 0;
+  strcat_disallowed = 1;
+
+  /* Test at least one instance of the __builtin_ style.  We do this
+     to ensure that it works and that the prototype is correct.  */
+  strcpy (dst, s1);
+  if (__builtin_strncat (dst, "", 100) != dst || strcmp (dst, s1))
+    abort ();
+
+  if (chk_calls)
+    abort ();
+  strncat_disallowed = 0;
+  strcat_disallowed = 0;
+}
+
+/* Test whether compile time checking is done where it should
+   and so is runtime object size checking.  */
+void
+__attribute__((noinline))
+test2 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4];
+  char buf3[20];
+  int i;
+
+  /* The following calls should do runtime checking.  */
+  memset (&a, '\0', sizeof (a));
+  s5 = (char *) &a;
+  __asm __volatile ("" : : "r" (s5) : "memory");
+  chk_calls = 0;
+  strncat (a.buf1 + 2, s3 + 3, l1 - 1);
+  strncat (r, s3 + 2, l1);
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  memset (r, '\0', 3);
+  __asm __volatile ("" : : "r" (r) : "memory");
+  strncat (r, s2 + 2, l1 + 1);
+  strncat (r + 2, s3 + 3, l1 - 1);
+  r = buf3;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1];
+      else if (i == l1)
+	r = &a.buf2[7];
+      else if (i == l1 + 1)
+	r = &buf3[5];
+      else if (i == l1 + 2)
+	r = &a.buf1[9];
+    }
+  strncat (r, s2 + 4, l1);
+  if (chk_calls != 5)
+    abort ();
+
+  /* Following have known destination and known source length,
+     but we don't know the length of dest string, so runtime checking
+     is needed too.  */
+  memset (&a, '\0', sizeof (a));
+  chk_calls = 0;
+  s5 = (char *) &a;
+  __asm __volatile ("" : : "r" (s5) : "memory");
+  strncat (a.buf1 + 2, "a", 5);
+  strncat (r, "def", 0);
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  memset (r, '\0', 3);
+  __asm __volatile ("" : : "r" (r) : "memory");
+  strncat (r, s1 + 1, 2);
+  if (chk_calls != 2)
+    abort ();
+  chk_calls = 0;
+  strcat_disallowed = 1;
+  /* Unknown destination and source, no checking.  */
+  strncat (s4, s3, l1 + 1);
+  strcat_disallowed = 0;
+  if (chk_calls)
+    abort ();
+}
+
+/* Test whether runtime and/or compile time checking catches
+   buffer overflows.  */
+void
+__attribute__((noinline))
+test3 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char buf3[20];
+
+  memset (&a, '\0', sizeof (a));
+  memset (buf3, '\0', sizeof (buf3));
+  s5 = (char *) &a;
+  __asm __volatile ("" : : "r" (s5) : "memory");
+  s5 = buf3;
+  __asm __volatile ("" : : "r" (s5) : "memory");
+  chk_fail_allowed = 1;
+  /* Runtime checks.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      strncat (&a.buf2[9], s2 + 3, 4);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      strncat (&a.buf2[7], s3 + strlen (s3) - 3, 3);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      strncat (&buf3[19], "abcde", 1);
+      abort ();
+    }
+  chk_fail_allowed = 0;
+}
+
+void
+main_test (void)
+{
+#ifndef __OPTIMIZE__
+  /* Object size checking is only intended for -O[s123].  */
+  return;
+#endif
+  __asm ("" : "=r" (s2) : "0" (s2));
+  __asm ("" : "=r" (s3) : "0" (s3));
+  __asm ("" : "=r" (l1) : "0" (l1));
+  s4 = p;
+  test1 ();
+  memset (p, '\0', sizeof (p));
+  test2 ();
+  test3 ();
+}
--- gcc/testsuite/gcc.c-torture/execute/builtins/memcpy-chk-lib.c.jj	2004-09-20 18:09:25.252571760 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/memcpy-chk-lib.c	2004-09-20 18:09:25.252571760 +0200
@@ -0,0 +1 @@
+#include "lib/chk.c"
--- gcc/testsuite/gcc.c-torture/execute/builtins/sprintf-chk.c.jj	2004-09-20 18:09:25.271568397 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/sprintf-chk.c	2004-09-20 18:09:25.271568397 +0200
@@ -0,0 +1,197 @@
+/* Copyright (C) 2004  Free Software Foundation.
+
+   Ensure builtin __sprintf_chk performs correctly.  */
+
+extern void abort (void);
+typedef __SIZE_TYPE__ size_t;
+extern size_t strlen(const char *);
+extern void *memcpy (void *, const void *, size_t);
+extern char *strcpy (char *, const char *);
+extern int memcmp (const void *, const void *, size_t);
+extern void *memset (void *, int, size_t);
+extern int sprintf (char *, const char *, ...);
+
+#include "chk.h"
+
+const char s1[] = "123";
+char p[32] = "";
+char *s2 = "defg";
+char *s3 = "FGH";
+char *s4;
+size_t l1 = 1;
+static char buffer[32];
+char *ptr = "barf";
+
+void
+__attribute__((noinline))
+test1 (void)
+{
+  chk_calls = 0;
+  sprintf_disallowed = 1;
+
+  memset (buffer, 'A', 32);
+  sprintf (buffer, "foo");
+  if (memcmp (buffer, "foo", 4) || buffer[4] != 'A')
+    abort ();
+
+  memset (buffer, 'A', 32);
+  if (sprintf (buffer, "foo") != 3)
+    abort ();
+  if (memcmp (buffer, "foo", 4) || buffer[4] != 'A')
+    abort ();
+
+  memset (buffer, 'A', 32);
+  sprintf (buffer, "%s", "bar");
+  if (memcmp (buffer, "bar", 4) || buffer[4] != 'A')
+    abort ();
+
+  memset (buffer, 'A', 32);
+  if (sprintf (buffer, "%s", "bar") != 3)
+    abort ();
+  if (memcmp (buffer, "bar", 4) || buffer[4] != 'A')
+    abort ();
+
+  if (chk_calls)
+    abort ();
+  sprintf_disallowed = 0;
+
+  memset (buffer, 'A', 32);
+  sprintf (buffer, "%s", ptr);
+  if (memcmp (buffer, "barf", 5) || buffer[5] != 'A')
+    abort ();
+
+  memset (buffer, 'A', 32);
+  sprintf (buffer, "%d - %c", (int) l1 + 27, *ptr);
+  if (memcmp (buffer, "28 - b\0AAAAA", 12))
+    abort ();
+
+  if (chk_calls != 2)
+    abort ();
+  chk_calls = 0;
+
+  sprintf (s4, "%d - %c", (int) l1 - 17, ptr[1]);
+  if (memcmp (s4, "-16 - a", 8))
+    abort ();
+  if (chk_calls)
+    abort ();
+}
+
+/* Test whether compile time checking is done where it should
+   and so is runtime object size checking.  */
+void
+__attribute__((noinline))
+test2 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4];
+  char buf3[20];
+  int i;
+
+  /* The following calls should do runtime checking
+     - source length is not known, but destination is.  */
+  chk_calls = 0;
+  sprintf (a.buf1 + 2, "%s", s3 + 3);
+  sprintf (r, "%s%c", s3 + 4, s3[3]);
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  sprintf (r, "%c %s", s2[2], s2 + 4);
+  sprintf (r + 2, s3 + 3);
+  r = buf3;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1];
+      else if (i == l1)
+	r = &a.buf2[7];
+      else if (i == l1 + 1)
+	r = &buf3[5];
+      else if (i == l1 + 2)
+	r = &a.buf1[9];
+    }
+  sprintf (r, s2 + 4);
+  if (chk_calls != 5)
+    abort ();
+
+  /* Following have known destination and known source length,
+     so if optimizing certainly shouldn't result in the checking
+     variants.  */
+  chk_calls = 0;
+  sprintf_disallowed = 1;
+  sprintf (a.buf1 + 2, "");
+  sprintf (r, "a");
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  sprintf (r, "%s", s1 + 1);
+  r = buf3;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1];
+      else if (i == l1)
+	r = &a.buf2[7];
+      else if (i == l1 + 1)
+	r = &buf3[5];
+      else if (i == l1 + 2)
+	r = &a.buf1[9];
+    }
+  sprintf (r, "%s", "");
+  sprintf_disallowed = 0;
+  /* Unknown destination and source, no checking.  */
+  sprintf (s4, "%s %d", s3, 0);
+  if (chk_calls)
+    abort ();
+}
+
+/* Test whether runtime and/or compile time checking catches
+   buffer overflows.  */
+void
+__attribute__((noinline))
+test3 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char buf3[20];
+
+  chk_fail_allowed = 1;
+  /* Runtime checks.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      sprintf (&a.buf2[9], "%c%s", s2[3], s2 + 4);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      sprintf (&a.buf2[7], "%s%c", s3 + strlen (s3) - 2, *s3);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      sprintf (&a.buf2[7], "%d", (int) l1 + 9999);
+      abort ();
+    }
+  /* This should be detectable at compile time already.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      sprintf (&buf3[19], "a");
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      sprintf (&buf3[17], "%s", "abc");
+      abort ();
+    }
+  chk_fail_allowed = 0;
+}
+
+void
+main_test (void)
+{
+#ifndef __OPTIMIZE__
+  /* Object size checking is only intended for -O[s123].  */
+  return;
+#endif
+  __asm ("" : "=r" (s2) : "0" (s2));
+  __asm ("" : "=r" (s3) : "0" (s3));
+  __asm ("" : "=r" (l1) : "0" (l1));
+  s4 = p;
+  test1 ();
+  test2 ();
+  test3 ();
+}
--- gcc/testsuite/gcc.c-torture/execute/builtins/memmove-chk-lib.c.jj	2004-09-20 18:09:25.248572468 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/memmove-chk-lib.c	2004-09-20 18:09:25.248572468 +0200
@@ -0,0 +1 @@
+#include "lib/chk.c"
--- gcc/testsuite/gcc.c-torture/execute/builtins/stpcpy-chk.c.jj	2004-09-20 18:09:25.253571583 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/stpcpy-chk.c	2004-09-20 18:09:25.253571583 +0200
@@ -0,0 +1,265 @@
+/* Copyright (C) 2004  Free Software Foundation.
+
+   Ensure builtin __stpcpy_chk performs correctly.  */
+
+extern void abort (void);
+typedef __SIZE_TYPE__ size_t;
+extern size_t strlen(const char *);
+extern void *memcpy (void *, const void *, size_t);
+extern char *stpcpy (char *, const char *);
+extern int memcmp (const void *, const void *, size_t);
+
+#include "chk.h"
+
+const char s1[] = "123";
+char p[32] = "";
+char *s2 = "defg";
+char *s3 = "FGH";
+char *s4;
+size_t l1 = 1;
+
+void
+__attribute__((noinline))
+test1 (void)
+{
+  int i = 8;
+
+#if defined __i386__ || defined __x86_64__
+  /* The functions below might not be optimized into direct stores on all
+     arches.  It depends on how many instructions would be generated and
+     what limits the architecture chooses in STORE_BY_PIECES_P.  */
+  stpcpy_disallowed = 1;
+#endif
+  if (stpcpy (p, "abcde") != p + 5 || memcmp (p, "abcde", 6))
+    abort ();
+  if (stpcpy (p + 16, "vwxyz" + 1) != p + 16 + 4 || memcmp (p + 16, "wxyz", 5))
+    abort ();
+  if (stpcpy (p + 1, "") != p + 1 + 0 || memcmp (p, "a\0cde", 6))
+    abort ();
+  if (stpcpy (p + 3, "fghij") != p + 3 + 5 || memcmp (p, "a\0cfghij", 9))
+    abort ();
+
+  if (stpcpy ((i++, p + 20 + 1), "23") != (p + 20 + 1 + 2)
+      || i != 9 || memcmp (p + 19, "z\0""23\0", 5))
+    abort ();
+
+  if (stpcpy (stpcpy (p, "ABCD"), "EFG") != p + 7 || memcmp (p, "ABCDEFG", 8))
+    abort();
+
+  /* Test at least one instance of the __builtin_ style.  We do this
+     to ensure that it works and that the prototype is correct.  */
+  if (__builtin_stpcpy (p, "abcde") != p + 5 || memcmp (p, "abcde", 6))
+    abort ();
+
+  /* If return value of stpcpy is ignored, it should be optimized into
+     strcpy call.  */
+  stpcpy_disallowed = 1;
+  stpcpy (p + 1, "abcd");
+  stpcpy_disallowed = 0;
+  if (memcmp (p, "aabcd", 6))
+    abort ();
+
+  if (chk_calls)
+    abort ();
+
+  chk_calls = 0;
+  strcpy_disallowed = 1;
+  if (stpcpy (p, s2) != p + 4 || memcmp (p, "defg\0", 6))
+    abort ();
+  strcpy_disallowed = 0;
+  stpcpy_disallowed = 1;
+  stpcpy (p + 2, s3);
+  stpcpy_disallowed = 0;
+  if (memcmp (p, "deFGH", 6))
+    abort ();
+  if (chk_calls != 2)
+    abort ();
+}
+
+#ifndef MAX_OFFSET
+#define MAX_OFFSET (sizeof (long long))
+#endif
+
+#ifndef MAX_COPY
+#define MAX_COPY (10 * sizeof (long long))
+#endif
+
+#ifndef MAX_EXTRA
+#define MAX_EXTRA (sizeof (long long))
+#endif
+
+#define MAX_LENGTH (MAX_OFFSET + MAX_COPY + 1 + MAX_EXTRA)
+
+/* Use a sequence length that is not divisible by two, to make it more
+   likely to detect when words are mixed up.  */
+#define SEQUENCE_LENGTH 31
+
+static union {
+  char buf[MAX_LENGTH];
+  long long align_int;
+  long double align_fp;
+} u1, u2;
+
+volatile char *vx;
+
+void
+__attribute__((noinline))
+test2 (void)
+{
+  int off1, off2, len, i;
+  char *p, *q, c;
+
+  for (off1 = 0; off1 < MAX_OFFSET; off1++)
+    for (off2 = 0; off2 < MAX_OFFSET; off2++)
+      for (len = 1; len < MAX_COPY; len++)
+	{
+	  for (i = 0, c = 'A'; i < MAX_LENGTH; i++, c++)
+	    {
+	      u1.buf[i] = 'a';
+	      if (c >= 'A' + SEQUENCE_LENGTH)
+		c = 'A';
+	      u2.buf[i] = c;
+	    }
+	  u2.buf[off2 + len] = '\0';
+
+	  p = stpcpy (u1.buf + off1, u2.buf + off2);
+	  if (p != u1.buf + off1 + len)
+	    abort ();
+
+	  q = u1.buf;
+	  for (i = 0; i < off1; i++, q++)
+	    if (*q != 'a')
+	      abort ();
+
+	  for (i = 0, c = 'A' + off2; i < len; i++, q++, c++)
+	    {
+	      if (c >= 'A' + SEQUENCE_LENGTH)
+		c = 'A';
+	      if (*q != c)
+		abort ();
+	    }
+
+	  if (*q++ != '\0')
+	    abort ();
+	  for (i = 0; i < MAX_EXTRA; i++, q++)
+	    if (*q != 'a')
+	      abort ();
+	}
+}
+
+/* Test whether compile time checking is done where it should
+   and so is runtime object size checking.  */
+void
+__attribute__((noinline))
+test3 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4];
+  char buf3[20];
+  int i;
+  const char *l;
+
+  /* The following calls should do runtime checking
+     - source length is not known, but destination is.  */
+  chk_calls = 0;
+  vx = stpcpy (a.buf1 + 2, s3 + 3);
+  vx = stpcpy (r, s3 + 2);
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  vx = stpcpy (r, s2 + 2);
+  vx = stpcpy (r + 2, s3 + 3);
+  r = buf3;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1];
+      else if (i == l1)
+	r = &a.buf2[7];
+      else if (i == l1 + 1)
+	r = &buf3[5];
+      else if (i == l1 + 2)
+	r = &a.buf1[9];
+    }
+  vx = stpcpy (r, s2 + 4);
+  if (chk_calls != 5)
+    abort ();
+
+  /* Following have known destination and known source length,
+     so if optimizing certainly shouldn't result in the checking
+     variants.  */
+  chk_calls = 0;
+  vx = stpcpy (a.buf1 + 2, "");
+  vx = stpcpy (r, "a");
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  vx = stpcpy (r, s1 + 1);
+  r = buf3;
+  l = "abc";
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1], l = "e";
+      else if (i == l1)
+	r = &a.buf2[7], l = "gh";
+      else if (i == l1 + 1)
+	r = &buf3[5], l = "jkl";
+      else if (i == l1 + 2)
+	r = &a.buf1[9], l = "";
+    }
+  vx = stpcpy (r, "");
+  /* Here, strlen (l) + 1 is known to be at most 4 and
+     __builtin_object_size (&buf3[16], 0) is 4, so this doesn't need
+     runtime checking.  */
+  vx = stpcpy (&buf3[16], l);
+  /* Unknown destination and source, no checking.  */
+  vx = stpcpy (s4, s3);
+  stpcpy (s4 + 4, s3);
+  if (chk_calls)
+    abort ();
+  chk_calls = 0;
+}
+
+/* Test whether runtime and/or compile time checking catches
+   buffer overflows.  */
+void
+__attribute__((noinline))
+test4 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char buf3[20];
+
+  chk_fail_allowed = 1;
+  /* Runtime checks.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      vx = stpcpy (&a.buf2[9], s2 + 3);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      vx = stpcpy (&a.buf2[7], s3 + strlen (s3) - 3);
+      abort ();
+    }
+  /* This should be detectable at compile time already.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      vx = stpcpy (&buf3[19], "a");
+      abort ();
+    }
+  chk_fail_allowed = 0;
+}
+
+void
+main_test (void)
+{
+#ifndef __OPTIMIZE__
+  /* Object size checking is only intended for -O[s123].  */
+  return;
+#endif
+  __asm ("" : "=r" (s2) : "0" (s2));
+  __asm ("" : "=r" (s3) : "0" (s3));
+  __asm ("" : "=r" (l1) : "0" (l1));
+  test1 ();
+  s4 = p;
+  test2 ();
+  test3 ();
+  test4 ();
+}
--- gcc/testsuite/gcc.c-torture/execute/builtins/vsnprintf-chk-lib.c.jj	2004-09-20 18:09:25.269568751 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/vsnprintf-chk-lib.c	2004-09-20 18:09:25.269568751 +0200
@@ -0,0 +1 @@
+#include "lib/chk.c"
--- gcc/testsuite/gcc.c-torture/execute/builtins/strncpy-chk.c.jj	2004-09-20 18:09:25.272568220 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/strncpy-chk.c	2004-09-20 18:09:25.272568220 +0200
@@ -0,0 +1,227 @@
+/* Copyright (C) 2004  Free Software Foundation.
+
+   Ensure builtin __strncpy_chk performs correctly.  */
+
+extern void abort (void);
+typedef __SIZE_TYPE__ size_t;
+extern size_t strlen(const char *);
+extern void *memcpy (void *, const void *, size_t);
+extern char *strncpy (char *, const char *, size_t);
+extern int memcmp (const void *, const void *, size_t);
+extern int strcmp (const char *, const char *);
+extern int strncmp (const char *, const char *, size_t);
+extern void *memset (void *, int, size_t);
+
+#include "chk.h"
+
+const char s1[] = "123";
+char p[32] = "";
+char *s2 = "defg";
+char *s3 = "FGH";
+char *s4;
+size_t l1 = 1;
+int i;
+
+void
+__attribute__((noinline))
+test1 (void)
+{
+  const char *const src = "hello world";
+  const char *src2;
+  char dst[64], *dst2;
+
+  strncpy_disallowed = 1;
+  chk_calls = 0;
+
+  memset (dst, 0, sizeof (dst));
+  if (strncpy (dst, src, 4) != dst || strncmp (dst, src, 4))
+    abort();
+
+  memset (dst, 0, sizeof (dst));
+  if (strncpy (dst+16, src, 4) != dst+16 || strncmp (dst+16, src, 4))
+    abort();
+
+  memset (dst, 0, sizeof (dst));
+  if (strncpy (dst+32, src+5, 4) != dst+32 || strncmp (dst+32, src+5, 4))
+    abort();
+
+  memset (dst, 0, sizeof (dst));
+  dst2 = dst;
+  if (strncpy (++dst2, src+5, 4) != dst+1 || strncmp (dst2, src+5, 4)
+      || dst2 != dst+1)
+    abort();
+
+  memset (dst, 0, sizeof (dst));
+  if (strncpy (dst, src, 0) != dst || strcmp (dst, ""))
+    abort();
+  
+  memset (dst, 0, sizeof (dst));
+  dst2 = dst; src2 = src;
+  if (strncpy (++dst2, ++src2, 0) != dst+1 || strcmp (dst2, "")
+      || dst2 != dst+1 || src2 != src+1)
+    abort();
+
+  memset (dst, 0, sizeof (dst));
+  dst2 = dst; src2 = src;
+  if (strncpy (++dst2+5, ++src2+5, 0) != dst+6 || strcmp (dst2+5, "")
+      || dst2 != dst+1 || src2 != src+1)
+    abort();
+
+  memset (dst, 0, sizeof (dst));
+  if (strncpy (dst, src, 12) != dst || strcmp (dst, src))
+    abort();
+
+  /* Test at least one instance of the __builtin_ style.  We do this
+     to ensure that it works and that the prototype is correct.  */
+  memset (dst, 0, sizeof (dst));
+  if (__builtin_strncpy (dst, src, 4) != dst || strncmp (dst, src, 4))
+    abort();
+
+  memset (dst, 0, sizeof (dst));
+  if (strncpy (dst, i++ ? "xfoo" + 1 : "bar", 4) != dst
+      || strcmp (dst, "bar")
+      || i != 1)
+    abort ();
+
+  if (chk_calls)
+    abort ();
+  strncpy_disallowed = 0;
+}
+
+void
+__attribute__((noinline))
+test2 (void)
+{
+  chk_calls = 0;
+  /* No runtime checking should be done here, both destination
+     and length are unknown.  */
+  strncpy (s4, "abcd", l1 + 1);
+  if (chk_calls)
+    abort ();
+}
+
+/* Test whether compile time checking is done where it should
+   and so is runtime object size checking.  */
+void
+__attribute__((noinline))
+test3 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4];
+  char buf3[20];
+  int i;
+  const char *l;
+  size_t l2;
+
+  /* The following calls should do runtime checking
+     - source length is not known, but destination is.  */
+  chk_calls = 0;
+  strncpy (a.buf1 + 2, s3 + 3, l1);
+  strncpy (r, s3 + 2, l1 + 2);
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  strncpy (r, s2 + 2, l1 + 2);
+  strncpy (r + 2, s3 + 3, l1);
+  r = buf3;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1];
+      else if (i == l1)
+	r = &a.buf2[7];
+      else if (i == l1 + 1)
+	r = &buf3[5];
+      else if (i == l1 + 2)
+	r = &a.buf1[9];
+    }
+  strncpy (r, s2 + 4, l1);
+  if (chk_calls != 5)
+    abort ();
+
+  /* Following have known destination and known length,
+     so if optimizing certainly shouldn't result in the checking
+     variants.  */
+  chk_calls = 0;
+  strncpy (a.buf1 + 2, "", 3);
+  strncpy (a.buf1 + 2, "", 0);
+  strncpy (r, "a", 1);
+  strncpy (r, "a", 3);
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  strncpy (r, s1 + 1, 3);
+  strncpy (r, s1 + 1, 2);
+  r = buf3;
+  l = "abc";
+  l2 = 4;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1], l = "e", l2 = 2;
+      else if (i == l1)
+	r = &a.buf2[7], l = "gh", l2 = 3;
+      else if (i == l1 + 1)
+	r = &buf3[5], l = "jkl", l2 = 4;
+      else if (i == l1 + 2)
+	r = &a.buf1[9], l = "", l2 = 1;
+    }
+  strncpy (r, "", 1);
+  /* Here, strlen (l) + 1 is known to be at most 4 and
+     __builtin_object_size (&buf3[16], 0) is 4, so this doesn't need
+     runtime checking.  */
+  strncpy (&buf3[16], l, l2);
+  strncpy (&buf3[15], "abc", l2);
+  strncpy (&buf3[10], "fghij", l2);
+  if (chk_calls)
+    abort ();
+  chk_calls = 0;
+}
+
+/* Test whether runtime and/or compile time checking catches
+   buffer overflows.  */
+void
+__attribute__((noinline))
+test4 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char buf3[20];
+
+  chk_fail_allowed = 1;
+  /* Runtime checks.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      strncpy (&a.buf2[9], s2 + 4, l1 + 1);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      strncpy (&a.buf2[7], s3, l1 + 4);
+      abort ();
+    }
+  /* This should be detectable at compile time already.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      strncpy (&buf3[19], "abc", 2);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      strncpy (&buf3[18], "", 3);
+      abort ();
+    }
+  chk_fail_allowed = 0;
+}
+
+void
+main_test (void)
+{
+#ifndef __OPTIMIZE__
+  /* Object size checking is only intended for -O[s123].  */
+  return;
+#endif
+  __asm ("" : "=r" (s2) : "0" (s2));
+  __asm ("" : "=r" (s3) : "0" (s3));
+  __asm ("" : "=r" (l1) : "0" (l1));
+  test1 ();
+  s4 = p;
+  test2 ();
+  test3 ();
+  test4 ();
+}
--- gcc/testsuite/gcc.c-torture/execute/builtins/vsnprintf-chk.c.jj	2004-09-20 18:09:25.259570521 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/vsnprintf-chk.c	2004-09-20 18:09:25.260570344 +0200
@@ -0,0 +1,321 @@
+/* Copyright (C) 2004  Free Software Foundation.
+
+   Ensure builtin __vsnprintf_chk performs correctly.  */
+
+#include <stdarg.h>
+
+extern void abort (void);
+typedef __SIZE_TYPE__ size_t;
+extern size_t strlen(const char *);
+extern void *memcpy (void *, const void *, size_t);
+extern char *strcpy (char *, const char *);
+extern int memcmp (const void *, const void *, size_t);
+extern void *memset (void *, int, size_t);
+extern int vsnprintf (char *, size_t, const char *, va_list);
+
+#include "chk.h"
+
+const char s1[] = "123";
+char p[32] = "";
+char *s2 = "defg";
+char *s3 = "FGH";
+char *s4;
+size_t l1 = 1;
+static char buffer[32];
+char *ptr = "barf";
+
+int
+__attribute__((noinline))
+test1_sub (int i, ...)
+{
+  int ret = 0;
+  va_list ap;
+  va_start (ap, i);
+  switch (i)
+    {
+    case 0:
+      vsnprintf (buffer, 4, "foo", ap);
+      break;
+    case 1:
+      ret = vsnprintf (buffer, 4, "foo bar", ap);
+      break;
+    case 2:
+      vsnprintf (buffer, 32, "%s", ap);
+      break;
+    case 3:
+      ret = vsnprintf (buffer, 21, "%s", ap);
+      break;
+    case 4:
+      ret = vsnprintf (buffer, 4, "%d%d%d", ap);
+      break;
+    case 5:
+      ret = vsnprintf (buffer, 32, "%d%d%d", ap);
+      break;
+    case 6:
+      ret = vsnprintf (buffer, strlen (ptr) + 1, "%s", ap);
+      break;
+    case 7:
+      vsnprintf (buffer, l1 + 31, "%d - %c", ap);
+      break;
+    case 8:
+      vsnprintf (s4, l1 + 6, "%d - %c", ap);
+      break;
+    }
+  va_end (ap);
+  return ret;
+}
+
+void
+__attribute__((noinline))
+test1 (void)
+{
+  chk_calls = 0;
+  /* vsnprintf_disallowed = 1; */
+
+  memset (buffer, 'A', 32);
+  test1_sub (0);
+  if (memcmp (buffer, "foo", 4) || buffer[4] != 'A')
+    abort ();
+
+  memset (buffer, 'A', 32);
+  if (test1_sub (1) != 7)
+    abort ();
+  if (memcmp (buffer, "foo", 4) || buffer[4] != 'A')
+    abort ();
+
+  vsnprintf_disallowed = 0;
+
+  memset (buffer, 'A', 32);
+  test1_sub (2, "bar");
+  if (memcmp (buffer, "bar", 4) || buffer[4] != 'A')
+    abort ();
+
+  memset (buffer, 'A', 32);
+  if (test1_sub (3, "bar") != 3)
+    abort ();
+  if (memcmp (buffer, "bar", 4) || buffer[4] != 'A')
+    abort ();
+
+  memset (buffer, 'A', 32);
+  if (test1_sub (4, (int) l1, (int) l1 + 1, (int) l1 + 12) != 4)
+    abort ();
+  if (memcmp (buffer, "121", 4) || buffer[4] != 'A')
+    abort ();
+
+  memset (buffer, 'A', 32);
+  if (test1_sub (5, (int) l1, (int) l1 + 1, (int) l1 + 12) != 4)
+    abort ();
+  if (memcmp (buffer, "1213", 5) || buffer[5] != 'A')
+    abort ();
+
+  if (chk_calls)
+    abort ();
+
+  memset (buffer, 'A', 32);
+  test1_sub (6, ptr);
+  if (memcmp (buffer, "barf", 5) || buffer[5] != 'A')
+    abort ();
+
+  memset (buffer, 'A', 32);
+  test1_sub (7, (int) l1 + 27, *ptr);
+  if (memcmp (buffer, "28 - b\0AAAAA", 12))
+    abort ();
+
+  if (chk_calls != 2)
+    abort ();
+  chk_calls = 0;
+
+  memset (s4, 'A', 32);
+  test1_sub (8, (int) l1 - 17, ptr[1]);
+  if (memcmp (s4, "-16 - \0AAA", 10))
+    abort ();
+  if (chk_calls)
+    abort ();
+}
+
+void
+__attribute__((noinline))
+test2_sub (int i, ...)
+{
+  va_list ap;
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4];
+  char buf3[20];
+  int j;
+
+  va_start (ap, i);
+  /* The following calls should do runtime checking
+     - length is not known, but destination is.  */
+  switch (i)
+    {
+    case 0:
+      vsnprintf (a.buf1 + 2, l1, "%s", ap);
+      break;
+    case 1:
+      vsnprintf (r, l1 + 4, "%s%c", ap);
+      break;
+    case 2:
+      r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+      vsnprintf (r, strlen (s2) - 2, "%c %s", ap);
+      break;
+    case 3:
+      r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+      vsnprintf (r + 2, l1, s3 + 3, ap);
+      break;
+    case 4:
+    case 7:
+      r = buf3;
+      for (j = 0; j < 4; ++j)
+	{
+	  if (j == l1 - 1)
+	    r = &a.buf1[1];
+	  else if (j == l1)
+	    r = &a.buf2[7];
+	  else if (j == l1 + 1)
+	    r = &buf3[5];
+	  else if (j == l1 + 2)
+	    r = &a.buf1[9];
+	}
+      if (i == 4)
+	vsnprintf (r, l1, s2 + 4, ap);
+      else
+	vsnprintf (r, 1, "a", ap);
+      break;
+    case 5:
+      r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+      vsnprintf (r, l1 + 3, "%s", ap);
+      break;
+    case 6:
+      vsnprintf (a.buf1 + 2, 4, "", ap);
+      break;
+    case 8:
+      vsnprintf (s4, 3, "%s %d", ap);
+      break;
+    }
+  va_end (ap);
+}
+
+/* Test whether compile time checking is done where it should
+   and so is runtime object size checking.  */
+void
+__attribute__((noinline))
+test2 (void)
+{
+  /* The following calls should do runtime checking
+     - length is not known, but destination is.  */
+  chk_calls = 0;
+  test2_sub (0, s3 + 3);
+  test2_sub (1, s3 + 4, s3[3]);
+  test2_sub (2, s2[2], s2 + 4);
+  test2_sub (3);
+  test2_sub (4);
+  test2_sub (5, s1 + 1);
+  if (chk_calls != 6)
+    abort ();
+
+  /* Following have known destination and known source length,
+     so if optimizing certainly shouldn't result in the checking
+     variants.  */
+  chk_calls = 0;
+  /* vsnprintf_disallowed = 1; */
+  test2_sub (6);
+  test2_sub (7);
+  vsnprintf_disallowed = 0;
+  /* Unknown destination and source, no checking.  */
+  test2_sub (8, s3, 0);
+  if (chk_calls)
+    abort ();
+}
+
+void
+__attribute__((noinline))
+test3_sub (int i, ...)
+{
+  va_list ap;
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char buf3[20];
+
+  va_start (ap, i);
+  /* The following calls should do runtime checking
+     - source length is not known, but destination is.  */
+  switch (i)
+    {
+    case 0:
+      vsnprintf (&a.buf2[9], l1 + 1, "%c%s", ap);
+      break;
+    case 1:
+      vsnprintf (&a.buf2[7], l1 + 30, "%s%c", ap);
+      break;
+    case 2:
+      vsnprintf (&a.buf2[7], l1 + 3, "%d", ap);
+      break;
+    case 3:
+      vsnprintf (&buf3[17], l1 + 3, "%s", ap);
+      break;
+    case 4:
+      vsnprintf (&buf3[19], 2, "a", ap);
+      break;
+    case 5:
+      vsnprintf (&buf3[16], 5, "a", ap);
+      break;
+    }
+  va_end (ap);
+}
+
+/* Test whether runtime and/or compile time checking catches
+   buffer overflows.  */
+void
+__attribute__((noinline))
+test3 (void)
+{
+  chk_fail_allowed = 1;
+  /* Runtime checks.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      test3_sub (0, s2[3], s2 + 4);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      test3_sub (1, s3 + strlen (s3) - 2, *s3);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      test3_sub (2, (int) l1 + 9999);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      test3_sub (3, "abc");
+      abort ();
+    }
+  /* This should be detectable at compile time already.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      test3_sub (4);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      test3_sub (5);
+      abort ();
+    }
+  chk_fail_allowed = 0;
+}
+
+void
+main_test (void)
+{
+#ifndef __OPTIMIZE__
+  /* Object size checking is only intended for -O[s123].  */
+  return;
+#endif
+  __asm ("" : "=r" (s2) : "0" (s2));
+  __asm ("" : "=r" (s3) : "0" (s3));
+  __asm ("" : "=r" (l1) : "0" (l1));
+  s4 = p;
+  test1 ();
+  test2 ();
+  test3 ();
+}
--- gcc/testsuite/gcc.c-torture/execute/builtins/chk.h.jj	2004-09-20 18:09:25.255571229 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/chk.h	2004-09-20 18:09:25.255571229 +0200
@@ -0,0 +1,81 @@
+#ifndef os
+# define os(ptr) __builtin_object_size (ptr, 0)
+#endif
+
+/* This is one of the alternatives for object size checking.
+   If dst has side-effects, size checking will never be done.  */
+#undef memcpy
+#define memcpy(dst, src, len) \
+  __builtin___memcpy_chk (dst, src, len, os (dst))
+#undef mempcpy
+#define mempcpy(dst, src, len) \
+  __builtin___mempcpy_chk (dst, src, len, os (dst))
+#undef memmove
+#define memmove(dst, src, len) \
+  __builtin___memmove_chk (dst, src, len, os (dst))
+#undef memset
+#define memset(dst, val, len) \
+  __builtin___memset_chk (dst, val, len, os (dst))
+#undef strcpy
+#define strcpy(dst, src) \
+  __builtin___strcpy_chk (dst, src, os (dst))
+#undef stpcpy
+#define stpcpy(dst, src) \
+  __builtin___stpcpy_chk (dst, src, os (dst))
+#undef strcat
+#define strcat(dst, src) \
+  __builtin___strcat_chk (dst, src, os (dst))
+#undef strncpy
+#define strncpy(dst, src, len) \
+  __builtin___strncpy_chk (dst, src, len, os (dst))
+#undef strncat
+#define strncat(dst, src, len) \
+  __builtin___strncat_chk (dst, src, len, os (dst))
+#undef sprintf
+#define sprintf(dst, ...) \
+  __builtin___sprintf_chk (dst, 0, os (dst), __VA_ARGS__)
+#undef vsprintf
+#define vsprintf(dst, fmt, ap) \
+  __builtin___vsprintf_chk (dst, 0, os (dst), fmt, ap)
+#undef snprintf
+#define snprintf(dst, len, ...) \
+  __builtin___snprintf_chk (dst, len, 0, os (dst), __VA_ARGS__)
+#undef vsnprintf
+#define vsnprintf(dst, len, fmt, ap) \
+  __builtin___vsnprintf_chk (dst, len, 0, os (dst), fmt, ap)
+
+/* Now "redefine" even builtins for the purpose of testing.  */
+#undef __builtin_memcpy
+#define __builtin_memcpy(dst, src, len) memcpy (dst, src, len)
+#undef __builtin_mempcpy
+#define __builtin_mempcpy(dst, src, len) mempcpy (dst, src, len)
+#undef __builtin_memmove
+#define __builtin_memmove(dst, src, len) memmove (dst, src, len)
+#undef __builtin_memset
+#define __builtin_memset(dst, val, len) memset (dst, val, len)
+#undef __builtin_strcpy
+#define __builtin_strcpy(dst, src) strcpy (dst, src)
+#undef __builtin_stpcpy
+#define __builtin_stpcpy(dst, src) stpcpy (dst, src)
+#undef __builtin_strcat
+#define __builtin_strcat(dst, src) strcat (dst, src)
+#undef __builtin_strncpy
+#define __builtin_strncpy(dst, src, len) strncpy (dst, src, len)
+#undef __builtin_strncat
+#define __builtin_strncat(dst, src, len) strncat (dst, src, len)
+#undef __builtin_sprintf
+#define __builtin_sprintf(dst, ...) sprintf (dst, __VA_ARGS__)
+#undef __builtin_vsprintf
+#define __builtin_vsprintf(dst, fmt, ap) vsprintf (dst, fmt, ap)
+#undef __builtin_snprintf
+#define __builtin_snprintf(dst, len, ...) snprintf (dst, len, __VA_ARGS__)
+#undef __builtin_vsnprintf
+#define __builtin_vsnprintf(dst, len, fmt, ap) vsnprintf (dst, len, fmt, ap)
+
+extern void *chk_fail_buf[];
+extern volatile int chk_fail_allowed, chk_calls;
+extern volatile int memcpy_disallowed, mempcpy_disallowed, memmove_disallowed;
+extern volatile int memset_disallowed, strcpy_disallowed, stpcpy_disallowed;
+extern volatile int strncpy_disallowed, strcat_disallowed, strncat_disallowed;
+extern volatile int sprintf_disallowed, vsprintf_disallowed;
+extern volatile int snprintf_disallowed, vsnprintf_disallowed;
--- gcc/testsuite/gcc.c-torture/execute/builtins/vsprintf-chk-lib.c.jj	2004-09-20 18:09:25.294564326 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/vsprintf-chk-lib.c	2004-09-20 18:09:25.294564326 +0200
@@ -0,0 +1 @@
+#include "lib/chk.c"
--- gcc/testsuite/gcc.c-torture/execute/builtins/strcpy-chk.c.jj	2004-09-20 18:09:25.229575831 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/strcpy-chk.c	2004-09-20 18:09:25.246572822 +0200
@@ -0,0 +1,230 @@
+/* Copyright (C) 2004  Free Software Foundation.
+
+   Ensure builtin __strcpy_chk performs correctly.  */
+
+extern void abort (void);
+typedef __SIZE_TYPE__ size_t;
+extern size_t strlen(const char *);
+extern void *memcpy (void *, const void *, size_t);
+extern char *strcpy (char *, const char *);
+extern int memcmp (const void *, const void *, size_t);
+
+#include "chk.h"
+
+const char s1[] = "123";
+char p[32] = "";
+char *s2 = "defg";
+char *s3 = "FGH";
+char *s4;
+size_t l1 = 1;
+
+void
+__attribute__((noinline))
+test1 (void)
+{
+  chk_calls = 0;
+  strcpy_disallowed = 1;
+
+  if (strcpy (p, "abcde") != p || memcmp (p, "abcde", 6))
+    abort ();
+  if (strcpy (p + 16, "vwxyz" + 1) != p + 16 || memcmp (p + 16, "wxyz", 5))
+    abort ();
+  if (strcpy (p + 1, "") != p + 1 || memcmp (p, "a\0cde", 6))
+    abort ();  
+  if (strcpy (p + 3, "fghij") != p + 3 || memcmp (p, "a\0cfghij", 9))
+    abort ();
+
+  /* Test at least one instance of the __builtin_ style.  We do this
+     to ensure that it works and that the prototype is correct.  */
+  if (__builtin_strcpy (p, "abcde") != p || memcmp (p, "abcde", 6))
+    abort ();
+
+  strcpy_disallowed = 0;
+  if (chk_calls)
+    abort ();
+}
+
+#ifndef MAX_OFFSET
+#define MAX_OFFSET (sizeof (long long))
+#endif
+
+#ifndef MAX_COPY
+#define MAX_COPY (10 * sizeof (long long))
+#endif
+
+#ifndef MAX_EXTRA
+#define MAX_EXTRA (sizeof (long long))
+#endif
+
+#define MAX_LENGTH (MAX_OFFSET + MAX_COPY + 1 + MAX_EXTRA)
+
+/* Use a sequence length that is not divisible by two, to make it more
+   likely to detect when words are mixed up.  */
+#define SEQUENCE_LENGTH 31
+
+static union {
+  char buf[MAX_LENGTH];
+  long long align_int;
+  long double align_fp;
+} u1, u2;
+
+void
+__attribute__((noinline))
+test2 (void)
+{
+  int off1, off2, len, i;
+  char *p, *q, c;
+
+  for (off1 = 0; off1 < MAX_OFFSET; off1++)
+    for (off2 = 0; off2 < MAX_OFFSET; off2++)
+      for (len = 1; len < MAX_COPY; len++)
+	{
+	  for (i = 0, c = 'A'; i < MAX_LENGTH; i++, c++)
+	    {
+	      u1.buf[i] = 'a';
+	      if (c >= 'A' + SEQUENCE_LENGTH)
+		c = 'A';
+	      u2.buf[i] = c;
+	    }
+	  u2.buf[off2 + len] = '\0';
+
+	  p = strcpy (u1.buf + off1, u2.buf + off2);
+	  if (p != u1.buf + off1)
+	    abort ();
+
+	  q = u1.buf;
+	  for (i = 0; i < off1; i++, q++)
+	    if (*q != 'a')
+	      abort ();
+
+	  for (i = 0, c = 'A' + off2; i < len; i++, q++, c++)
+	    {
+	      if (c >= 'A' + SEQUENCE_LENGTH)
+		c = 'A';
+	      if (*q != c)
+		abort ();
+	    }
+
+	  if (*q++ != '\0')
+	    abort ();
+	  for (i = 0; i < MAX_EXTRA; i++, q++)
+	    if (*q != 'a')
+	      abort ();
+	}
+}
+
+/* Test whether compile time checking is done where it should
+   and so is runtime object size checking.  */
+void
+__attribute__((noinline))
+test3 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4];
+  char buf3[20];
+  int i;
+  const char *l;
+
+  /* The following calls should do runtime checking
+     - source length is not known, but destination is.  */
+  chk_calls = 0;
+  strcpy (a.buf1 + 2, s3 + 3);
+  strcpy (r, s3 + 2);
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  strcpy (r, s2 + 2);
+  strcpy (r + 2, s3 + 3);
+  r = buf3;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1];
+      else if (i == l1)
+	r = &a.buf2[7];
+      else if (i == l1 + 1)
+	r = &buf3[5];
+      else if (i == l1 + 2)
+	r = &a.buf1[9];
+    }
+  strcpy (r, s2 + 4);
+  if (chk_calls != 5)
+    abort ();
+
+  /* Following have known destination and known source length,
+     so if optimizing certainly shouldn't result in the checking
+     variants.  */
+  chk_calls = 0;
+  strcpy (a.buf1 + 2, "");
+  strcpy (r, "a");
+  r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
+  strcpy (r, s1 + 1);
+  r = buf3;
+  l = "abc";
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	r = &a.buf1[1], l = "e";
+      else if (i == l1)
+	r = &a.buf2[7], l = "gh";
+      else if (i == l1 + 1)
+	r = &buf3[5], l = "jkl";
+      else if (i == l1 + 2)
+	r = &a.buf1[9], l = "";
+    }
+  strcpy (r, "");
+  /* Here, strlen (l) + 1 is known to be at most 4 and
+     __builtin_object_size (&buf3[16], 0) is 4, so this doesn't need
+     runtime checking.  */
+  strcpy (&buf3[16], l);
+  /* Unknown destination and source, no checking.  */
+  strcpy (s4, s3);
+  if (chk_calls)
+    abort ();
+  chk_calls = 0;
+}
+
+/* Test whether runtime and/or compile time checking catches
+   buffer overflows.  */
+void
+__attribute__((noinline))
+test4 (void)
+{
+  struct A { char buf1[10]; char buf2[10]; } a;
+  char buf3[20];
+
+  chk_fail_allowed = 1;
+  /* Runtime checks.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      strcpy (&a.buf2[9], s2 + 3);
+      abort ();
+    }
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      strcpy (&a.buf2[7], s3 + strlen (s3) - 3);
+      abort ();
+    }
+  /* This should be detectable at compile time already.  */
+  if (__builtin_setjmp (chk_fail_buf) == 0)
+    {
+      strcpy (&buf3[19], "a");
+      abort ();
+    }
+  chk_fail_allowed = 0;
+}
+
+void
+main_test (void)
+{
+#ifndef __OPTIMIZE__
+  /* Object size checking is only intended for -O[s123].  */
+  return;
+#endif
+  __asm ("" : "=r" (s2) : "0" (s2));
+  __asm ("" : "=r" (s3) : "0" (s3));
+  __asm ("" : "=r" (l1) : "0" (l1));
+  test1 ();
+  test2 ();
+  s4 = p;
+  test3 ();
+  test4 ();
+}
--- gcc/testsuite/gcc.c-torture/execute/builtins/sprintf-chk-lib.c.jj	2004-09-20 18:09:25.268568928 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/sprintf-chk-lib.c	2004-09-20 18:09:25.268568928 +0200
@@ -0,0 +1 @@
+#include "lib/chk.c"
--- gcc/testsuite/gcc.c-torture/execute/builtins/lib/chk.c.jj	2004-09-20 18:09:25.278567158 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/lib/chk.c	2004-09-20 18:09:25.279566981 +0200
@@ -0,0 +1,472 @@
+#include <stdarg.h>
+
+extern void abort (void);
+
+extern int inside_main;
+void *chk_fail_buf[256] __attribute__((aligned (16)));
+volatile int chk_fail_allowed, chk_calls;
+volatile int memcpy_disallowed, mempcpy_disallowed, memmove_disallowed;
+volatile int memset_disallowed, strcpy_disallowed, stpcpy_disallowed;
+volatile int strncpy_disallowed, strcat_disallowed, strncat_disallowed;
+volatile int sprintf_disallowed, vsprintf_disallowed;
+volatile int snprintf_disallowed, vsnprintf_disallowed;
+extern __SIZE_TYPE__ strlen (const char *);
+extern int vsprintf (char *, const char *, va_list);
+
+void __attribute__((noreturn))
+__chk_fail (void)
+{
+  if (chk_fail_allowed)
+    __builtin_longjmp (chk_fail_buf, 1);
+  abort ();
+}
+
+void *
+memcpy (void *dst, const void *src, __SIZE_TYPE__ n)
+{
+  const char *srcp;
+  char *dstp;
+
+#ifdef __OPTIMIZE__
+  if (memcpy_disallowed && inside_main)
+    abort ();
+#endif
+
+  srcp = src;
+  dstp = dst;
+  while (n-- != 0)
+    *dstp++ = *srcp++;
+
+  return dst;
+}
+
+void *
+__memcpy_chk (void *dst, const void *src, __SIZE_TYPE__ n, __SIZE_TYPE__ size)
+{
+  /* If size is -1, GCC should always optimize the call into memcpy.  */
+  if (size == (__SIZE_TYPE__) -1)
+    abort ();
+  ++chk_calls;
+  if (n > size)
+    __chk_fail ();
+  return memcpy (dst, src, n);
+}
+
+void *
+mempcpy (void *dst, const void *src, __SIZE_TYPE__ n)
+{
+  const char *srcp;
+  char *dstp;
+
+#ifdef __OPTIMIZE__
+  if (mempcpy_disallowed && inside_main)
+    abort ();
+#endif
+
+  srcp = src;
+  dstp = dst;
+  while (n-- != 0)
+    *dstp++ = *srcp++;
+
+  return dstp;
+}
+
+void *
+__mempcpy_chk (void *dst, const void *src, __SIZE_TYPE__ n, __SIZE_TYPE__ size)
+{
+  /* If size is -1, GCC should always optimize the call into mempcpy.  */
+  if (size == (__SIZE_TYPE__) -1)
+    abort ();
+  ++chk_calls;
+  if (n > size)
+    __chk_fail ();
+  return mempcpy (dst, src, n);
+}
+
+void *
+memmove (void *dst, const void *src, __SIZE_TYPE__ n)
+{
+  const char *srcp;
+  char *dstp;
+
+#ifdef __OPTIMIZE__
+  if (memmove_disallowed && inside_main)
+    abort ();
+#endif
+
+  srcp = src;
+  dstp = dst;
+  if (srcp < dstp)
+    while (n-- != 0)
+      dstp[n] = srcp[n];
+  else
+    while (n-- != 0)
+      *dstp++ = *srcp++;
+
+  return dst;
+}
+
+void *
+__memmove_chk (void *dst, const void *src, __SIZE_TYPE__ n, __SIZE_TYPE__ size)
+{
+  /* If size is -1, GCC should always optimize the call into memmove.  */
+  if (size == (__SIZE_TYPE__) -1)
+    abort ();
+  ++chk_calls;
+  if (n > size)
+    __chk_fail ();
+  return memmove (dst, src, n);
+}
+
+void *
+memset (void *dst, int c, __SIZE_TYPE__ n)
+{
+  /* Single-byte memsets should be done inline when optimisation
+     is enabled.  */
+#ifdef __OPTIMIZE__
+  if (memset_disallowed && inside_main && n < 2)
+    abort ();
+#endif
+
+  while (n-- != 0)
+    n[(char *) dst] = c;
+
+  return dst;
+}
+
+void *
+__memset_chk (void *dst, int c, __SIZE_TYPE__ n, __SIZE_TYPE__ size)
+{
+  /* If size is -1, GCC should always optimize the call into memset.  */
+  if (size == (__SIZE_TYPE__) -1)
+    abort ();
+  ++chk_calls;
+  if (n > size)
+    __chk_fail ();
+  return memset (dst, c, n);
+}
+
+char *
+strcpy (char *d, const char *s)
+{
+  char *r = d;
+#ifdef __OPTIMIZE__
+  if (strcpy_disallowed && inside_main)
+    abort ();
+#endif
+  while ((*d++ = *s++));
+  return r;
+}
+
+char *
+__strcpy_chk (char *d, const char *s, __SIZE_TYPE__ size)
+{
+  /* If size is -1, GCC should always optimize the call into strcpy.  */
+  if (size == (__SIZE_TYPE__) -1)
+    abort ();
+  ++chk_calls;
+  if (strlen (s) >= size)
+    __chk_fail ();
+  return strcpy (d, s);
+}
+
+char *
+stpcpy (char *dst, const char *src)
+{
+#ifdef __OPTIMIZE__
+  if (stpcpy_disallowed && inside_main)
+    abort ();
+#endif
+
+  while (*src != 0)
+    *dst++ = *src++;
+
+  *dst = 0;
+  return dst;
+}
+
+char *
+__stpcpy_chk (char *d, const char *s, __SIZE_TYPE__ size)
+{
+  /* If size is -1, GCC should always optimize the call into stpcpy.  */
+  if (size == (__SIZE_TYPE__) -1)
+    abort ();
+  ++chk_calls;
+  if (strlen (s) >= size)
+    __chk_fail ();
+  return stpcpy (d, s);
+}
+
+char *
+strncpy (char *s1, const char *s2, __SIZE_TYPE__ n)
+{
+  char *dest = s1;
+#ifdef __OPTIMIZE__
+  if (strncpy_disallowed && inside_main)
+    abort();
+#endif
+  for (; *s2 && n; n--)
+    *s1++ = *s2++;
+  while (n--)
+    *s1++ = 0;
+  return dest;
+}
+
+char *
+__strncpy_chk (char *s1, const char *s2, __SIZE_TYPE__ n, __SIZE_TYPE__ size)
+{
+  /* If size is -1, GCC should always optimize the call into strncpy.  */
+  if (size == (__SIZE_TYPE__) -1)
+    abort ();
+  ++chk_calls;
+  if (n > size)
+    __chk_fail ();
+  return strncpy (s1, s2, n);
+}
+
+char *
+strcat (char *dst, const char *src)
+{
+  char *p = dst;
+  
+#ifdef __OPTIMIZE__
+  if (strcat_disallowed && inside_main)
+    abort ();
+#endif
+
+  while (*p)
+    p++;
+  while ((*p++ = *src++))
+    ;
+  return dst;
+}
+
+char *
+__strcat_chk (char *d, const char *s, __SIZE_TYPE__ size)
+{
+  /* If size is -1, GCC should always optimize the call into strcat.  */
+  if (size == (__SIZE_TYPE__) -1)
+    abort ();
+  ++chk_calls;
+  if (strlen (d) + strlen (s) >= size)
+    __chk_fail ();
+  return strcat (d, s);
+}
+
+char *
+strncat (char *s1, const char *s2, __SIZE_TYPE__ n)
+{
+  char *dest = s1;
+  char c;
+#ifdef __OPTIMIZE__
+  if (strncat_disallowed && inside_main)
+    abort();
+#endif
+  while (*s1) s1++;
+  c = '\0';
+  while (n > 0)
+    {
+      c = *s2++;
+      *s1++ = c;
+      if (c == '\0')
+	return dest;
+      n--;
+    }
+  if (c != '\0')
+    *s1 = '\0';
+  return dest;
+}
+
+char *
+__strncat_chk (char *d, const char *s, __SIZE_TYPE__ n, __SIZE_TYPE__ size)
+{
+  __SIZE_TYPE__ len = strlen (d), n1 = n;
+  const char *s1 = s;
+
+  /* If size is -1, GCC should always optimize the call into strncat.  */
+  if (size == (__SIZE_TYPE__) -1)
+    abort ();
+  ++chk_calls;
+  while (len < size && n1 > 0)
+    {
+      if (*s1++ == '\0')
+	break;
+      ++len;
+      --n1;
+    }
+
+  if (len >= size)
+    __chk_fail ();
+  return strncat (d, s, n);
+}
+
+/* No chk test in GCC testsuite needs more bytes than this.
+   As we can't expect vsnprintf to be available on the target,
+   assume 4096 bytes is enough.  */
+static char chk_sprintf_buf[4096];
+
+int
+__sprintf_chk (char *str, int flag, __SIZE_TYPE__ size, const char *fmt, ...)
+{
+  int ret;
+  va_list ap;
+
+  /* If size is -1 and flag 0, GCC should always optimize the call into
+     sprintf.  */
+  if (size == (__SIZE_TYPE__) -1 && flag == 0)
+    abort ();
+  ++chk_calls;
+#ifdef __OPTIMIZE__
+  if (sprintf_disallowed && inside_main)
+    abort();
+#endif
+  va_start (ap, fmt);
+  ret = vsprintf (chk_sprintf_buf, fmt, ap);
+  va_end (ap);
+  if (ret >= 0)
+    {
+      if (ret >= size)
+	__chk_fail ();
+      memcpy (str, chk_sprintf_buf, ret + 1);
+    }
+  return ret;
+}
+
+int
+__vsprintf_chk (char *str, int flag, __SIZE_TYPE__ size, const char *fmt,
+		va_list ap)
+{
+  int ret;
+
+  /* If size is -1 and flag 0, GCC should always optimize the call into
+     vsprintf.  */
+  if (size == (__SIZE_TYPE__) -1 && flag == 0)
+    abort ();
+  ++chk_calls;
+#ifdef __OPTIMIZE__
+  if (vsprintf_disallowed && inside_main)
+    abort();
+#endif
+  ret = vsprintf (chk_sprintf_buf, fmt, ap);
+  if (ret >= 0)
+    {
+      if (ret >= size)
+	__chk_fail ();
+      memcpy (str, chk_sprintf_buf, ret + 1);
+    }
+  return ret;
+}
+
+int
+__snprintf_chk (char *str, __SIZE_TYPE__ len, int flag, __SIZE_TYPE__ size,
+		const char *fmt, ...)
+{
+  int ret;
+  va_list ap;
+
+  /* If size is -1 and flag 0, GCC should always optimize the call into
+     snprintf.  */
+  if (size == (__SIZE_TYPE__) -1 && flag == 0)
+    abort ();
+  ++chk_calls;
+  if (size < len)
+    __chk_fail ();
+#ifdef __OPTIMIZE__
+  if (snprintf_disallowed && inside_main)
+    abort();
+#endif
+  va_start (ap, fmt);
+  ret = vsprintf (chk_sprintf_buf, fmt, ap);
+  va_end (ap);
+  if (ret >= 0)
+    {
+      if (ret < len)
+	memcpy (str, chk_sprintf_buf, ret + 1);
+      else
+	{
+	  memcpy (str, chk_sprintf_buf, len - 1);
+	  str[len - 1] = '\0';
+	}
+    }
+  return ret;
+}
+
+int
+__vsnprintf_chk (char *str, __SIZE_TYPE__ len, int flag, __SIZE_TYPE__ size,
+		 const char *fmt, va_list ap)
+{
+  int ret;
+
+  /* If size is -1 and flag 0, GCC should always optimize the call into
+     vsnprintf.  */
+  if (size == (__SIZE_TYPE__) -1 && flag == 0)
+    abort ();
+  ++chk_calls;
+  if (size < len)
+    __chk_fail ();
+#ifdef __OPTIMIZE__
+  if (vsnprintf_disallowed && inside_main)
+    abort();
+#endif
+  ret = vsprintf (chk_sprintf_buf, fmt, ap);
+  if (ret >= 0)
+    {
+      if (ret < len)
+	memcpy (str, chk_sprintf_buf, ret + 1);
+      else
+	{
+	  memcpy (str, chk_sprintf_buf, len - 1);
+	  str[len - 1] = '\0';
+	}
+    }
+  return ret;
+}
+
+int
+snprintf (char *str, __SIZE_TYPE__ len, const char *fmt, ...)
+{
+  int ret;
+  va_list ap;
+
+#ifdef __OPTIMIZE__
+  if (snprintf_disallowed && inside_main)
+    abort();
+#endif
+  va_start (ap, fmt);
+  ret = vsprintf (chk_sprintf_buf, fmt, ap);
+  va_end (ap);
+  if (ret >= 0)
+    {
+      if (ret < len)
+	memcpy (str, chk_sprintf_buf, ret + 1);
+      else if (len)
+	{
+	  memcpy (str, chk_sprintf_buf, len - 1);
+	  str[len - 1] = '\0';
+	}
+    }
+  return ret;
+}
+
+int
+vsnprintf (char *str, __SIZE_TYPE__ len, const char *fmt, va_list ap)
+{
+  int ret;
+
+#ifdef __OPTIMIZE__
+  if (vsnprintf_disallowed && inside_main)
+    abort();
+#endif
+  ret = vsprintf (chk_sprintf_buf, fmt, ap);
+  if (ret >= 0)
+    {
+      if (ret < len)
+	memcpy (str, chk_sprintf_buf, ret + 1);
+      else if (len)
+	{
+	  memcpy (str, chk_sprintf_buf, len - 1);
+	  str[len - 1] = '\0';
+	}
+    }
+  return ret;
+}
--- gcc/testsuite/gcc.c-torture/execute/builtins/lib/main.c.jj	2004-07-03 18:44:13.000000000 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/lib/main.c	2004-09-20 18:09:25.277567335 +0200
@@ -1,4 +1,5 @@
 extern void main_test (void);
+extern void abort (void);
 int inside_main;
 
 int
--- gcc/testsuite/gcc.c-torture/execute/builtins/lib/strncat.c.jj	2004-07-03 04:16:50.000000000 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/lib/strncat.c	2004-09-20 18:09:25.277567335 +0200
@@ -12,11 +12,12 @@ strncat (char *s1, const char *s2, size_
     abort();
 #endif
   while (*s1) s1++;
+  c = '\0';
   while (n > 0)
     {
       c = *s2++;
       *s1++ = c;
-      if (c == 0)
+      if (c == '\0')
 	return dest;
       n--;
     }
--- gcc/testsuite/gcc.c-torture/execute/builtins/snprintf-chk-lib.c.jj	2004-09-20 18:09:25.263569813 +0200
+++ gcc/testsuite/gcc.c-torture/execute/builtins/snprintf-chk-lib.c	2004-09-20 18:09:25.264569636 +0200
@@ -0,0 +1 @@
+#include "lib/chk.c"
--- gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-1.c.jj	2004-09-20 18:09:25.308561849 +0200
+++ gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-1.c	2004-09-20 18:09:25.308561849 +0200
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-fab" } */
+
+#include <stdarg.h>
+
+typedef struct { int i; } FILE;
+FILE *fp;
+extern int vfprintf (FILE *, const char *, va_list);
+volatile int vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7, vi8, vi9, via;
+
+void test (va_list ap)
+{
+  vi0 = 0;
+  vi1 = vfprintf (fp, "hello", ap);
+  vi2 = vfprintf (fp, "hello\n", ap);
+  vi3 = vfprintf (fp, "a", ap);
+  vi4 = vfprintf (fp, "", ap);
+  vi5 = vfprintf (fp, "%s", ap);
+  vi6 = vfprintf (fp, "%c", ap);
+  vi7 = vfprintf (fp, "%s\n", ap);
+}
+
+/* { dg-final { scan-tree-dump "vi0.*fputs.*\"hello\".*fp.*vi1" "fab"} } */
+/* { dg-final { scan-tree-dump "vi1.*fputs.*\"hello\\\\n\".*fp.*vi2" "fab"} } */
+/* { dg-final { scan-tree-dump "vi2.*fputs.*\"a\".*fp.*vi3" "fab"} } */
+/* { dg-final { scan-tree-dump "vi4 = 0" "fab"} } */
+/* { dg-final { scan-tree-dump "vi4.*vfprintf.*\"%s\".*vi5" "fab"} } */
+/* { dg-final { scan-tree-dump "vi5.*vfprintf.*\"%c\".*vi6" "fab"} } */
+/* { dg-final { scan-tree-dump "vi6.*vfprintf.*\"%s\\\\n\".*vi7" "fab"} } */
--- gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-1.c.jj	2004-09-20 18:09:25.300563264 +0200
+++ gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-1.c	2004-09-20 18:09:25.300563264 +0200
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-fab" } */
+
+typedef struct { int i; } FILE;
+FILE *fp;
+extern int fprintf (FILE *, const char *, ...);
+volatile int vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7, vi8, vi9;
+
+void test (void)
+{
+  vi0 = 0;
+  vi1 = fprintf (fp, "hello");
+  vi2 = fprintf (fp, "hello\n");
+  vi3 = fprintf (fp, "a");
+  vi4 = fprintf (fp, "");
+  vi5 = fprintf (fp, "%s", "hello");
+  vi6 = fprintf (fp, "%s", "hello\n");
+  vi7 = fprintf (fp, "%s", "a");
+  vi8 = fprintf (fp, "%c", 'x');
+  vi9 = fprintf (fp, "%d%d", vi0, vi1);
+}
+
+/* { dg-final { scan-tree-dump "vi0.*fputs.*\"hello\".*fp.*vi1" "fab"} } */
+/* { dg-final { scan-tree-dump "vi1.*fputs.*\"hello\\\\n\".*fp.*vi2" "fab"} } */
+/* { dg-final { scan-tree-dump "vi2.*fputs.*\"a\".*fp.*vi3" "fab"} } */
+/* { dg-final { scan-tree-dump "vi4 = 0" "fab"} } */
+/* { dg-final { scan-tree-dump "vi4.*fputs.*\"hello\".*fp.*vi5" "fab"} } */
+/* { dg-final { scan-tree-dump "vi5.*fputs.*\"hello\\\\n\".*fp.*vi6" "fab"} } */
+/* { dg-final { scan-tree-dump "vi6.*fputs.*\"a\".*fp.*vi7" "fab"} } */
+/* { dg-final { scan-tree-dump "vi7.*fputc.*fp.*vi8" "fab"} } */
+/* { dg-final { scan-tree-dump "vi8.*fprintf.*fp.*\"%d%d\".*vi9" "fab"} } */
--- gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-chk-1.c.jj	2004-09-20 18:09:25.299563441 +0200
+++ gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-chk-1.c	2004-09-20 18:09:25.299563441 +0200
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-fab" } */
+
+#include <stdarg.h>
+
+typedef struct { int i; } FILE;
+FILE *fp;
+extern int __vfprintf_chk (FILE *, int, const char *, va_list);
+volatile int vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7, vi8, vi9, via;
+
+void test (va_list ap)
+{
+  vi0 = 0;
+  vi1 = __vfprintf_chk (fp, 1, "hello", ap);
+  vi2 = __vfprintf_chk (fp, 1, "hello\n", ap);
+  vi3 = __vfprintf_chk (fp, 1, "a", ap);
+  vi4 = __vfprintf_chk (fp, 1, "", ap);
+  vi5 = __vfprintf_chk (fp, 1, "%s", ap);
+  vi6 = __vfprintf_chk (fp, 1, "%c", ap);
+  vi7 = __vfprintf_chk (fp, 1, "%s\n", ap);
+}
+
+/* { dg-final { scan-tree-dump "vi0.*fputs.*\"hello\".*fp.*vi1" "fab"} } */
+/* { dg-final { scan-tree-dump "vi1.*fputs.*\"hello\\\\n\".*fp.*vi2" "fab"} } */
+/* { dg-final { scan-tree-dump "vi2.*fputs.*\"a\".*fp.*vi3" "fab"} } */
+/* { dg-final { scan-tree-dump "vi4 = 0" "fab"} } */
+/* { dg-final { scan-tree-dump "vi4.*__vfprintf_chk.*fp.*1.*\"%s\".*vi5" "fab"} } */
+/* { dg-final { scan-tree-dump "vi5.*__vfprintf_chk.*fp.*1.*\"%c\".*vi6" "fab"} } */
+/* { dg-final { scan-tree-dump "vi6.*__vfprintf_chk.*fp.*1.*\"%s\\\\n\".*vi7" "fab"} } */
--- gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-1.c.jj	2004-09-20 18:09:25.301563087 +0200
+++ gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-1.c	2004-09-20 18:09:25.301563087 +0200
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-fab" } */
+
+extern int printf (const char *, ...);
+volatile int vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7, vi8, vi9, via;
+
+void test (void)
+{
+  vi0 = 0;
+  vi1 = printf ("hello");
+  vi2 = printf ("hello\n");
+  vi3 = printf ("a");
+  vi4 = printf ("");
+  vi5 = printf ("%s", "hello");
+  vi6 = printf ("%s", "hello\n");
+  vi7 = printf ("%s", "a");
+  vi8 = printf ("%s", "");
+  vi9 = printf ("%c", 'x');
+  via = printf ("%s\n", "hello\n");
+}
+
+/* { dg-final { scan-tree-dump "vi0.*printf.*\"hello\".*vi1" "fab"} } */
+/* { dg-final { scan-tree-dump "vi1.*puts.*\"hello\".*vi2" "fab"} } */
+/* { dg-final { scan-tree-dump "vi2.*putchar.*vi3" "fab"} } */
+/* { dg-final { scan-tree-dump "vi4 = 0" "fab"} } */
+/* { dg-final { scan-tree-dump "vi4.*printf.*\"hello\".*vi5" "fab"} } */
+/* { dg-final { scan-tree-dump "vi5.*puts.*\"hello\".*vi6" "fab"} } */
+/* { dg-final { scan-tree-dump "vi6.*putchar.*vi7" "fab"} } */
+/* { dg-final { scan-tree-dump "vi8 = 0" "fab"} } */
+/* { dg-final { scan-tree-dump "vi8.*putchar.*vi9" "fab"} } */
+/* { dg-final { scan-tree-dump "vi9.*puts.*\"hello\\\\n\".*via" "fab"} } */
--- gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-chk-1.c.jj	2004-09-20 18:09:25.304562556 +0200
+++ gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-chk-1.c	2004-09-20 18:09:25.304562556 +0200
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-fab" } */
+
+extern int __printf_chk (int, const char *, ...);
+volatile int vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7, vi8, vi9, via;
+
+void test (void)
+{
+  vi0 = 0;
+  vi1 = __printf_chk (1, "hello");
+  vi2 = __printf_chk (1, "hello\n");
+  vi3 = __printf_chk (1, "a");
+  vi4 = __printf_chk (1, "");
+  vi5 = __printf_chk (1, "%s", "hello");
+  vi6 = __printf_chk (1, "%s", "hello\n");
+  vi7 = __printf_chk (1, "%s", "a");
+  vi8 = __printf_chk (1, "%s", "");
+  vi9 = __printf_chk (1, "%c", 'x');
+  via = __printf_chk (1, "%s\n", "hello\n");
+}
+
+/* { dg-final { scan-tree-dump "vi0.*__printf_chk.*1.*\"hello\".*vi1" "fab"} } */
+/* { dg-final { scan-tree-dump "vi1.*puts.*\"hello\".*vi2" "fab"} } */
+/* { dg-final { scan-tree-dump "vi2.*putchar.*vi3" "fab"} } */
+/* { dg-final { scan-tree-dump "vi4 = 0" "fab"} } */
+/* { dg-final { scan-tree-dump "vi4.*__printf_chk.*1.*\"hello\".*vi5" "fab"} } */
+/* { dg-final { scan-tree-dump "vi5.*puts.*\"hello\".*vi6" "fab"} } */
+/* { dg-final { scan-tree-dump "vi6.*putchar.*vi7" "fab"} } */
+/* { dg-final { scan-tree-dump "vi8 = 0" "fab"} } */
+/* { dg-final { scan-tree-dump "vi8.*putchar.*vi9" "fab"} } */
+/* { dg-final { scan-tree-dump "vi9.*puts.*\"hello\\\\n\".*via" "fab"} } */
--- gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-1.c.jj	2004-09-20 18:09:25.305562379 +0200
+++ gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-1.c	2004-09-20 18:09:25.305562379 +0200
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-fab" } */
+
+#include <stdarg.h>
+
+extern int vprintf (const char *, va_list);
+volatile int vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7, vi8, vi9, via;
+
+void test (va_list ap)
+{
+  vi0 = 0;
+  vi1 = vprintf ("hello", ap);
+  vi2 = vprintf ("hello\n", ap);
+  vi3 = vprintf ("a", ap);
+  vi4 = vprintf ("", ap);
+  vi5 = vprintf ("%s", ap);
+  vi6 = vprintf ("%c", ap);
+  vi7 = vprintf ("%s\n", ap);
+}
+
+/* { dg-final { scan-tree-dump "vi0.*vprintf.*\"hello\".*vi1" "fab"} } */
+/* { dg-final { scan-tree-dump "vi1.*puts.*\"hello\".*vi2" "fab"} } */
+/* { dg-final { scan-tree-dump "vi2.*putchar.*vi3" "fab"} } */
+/* { dg-final { scan-tree-dump "vi4 = 0" "fab"} } */
+/* { dg-final { scan-tree-dump "vi4.*vprintf.*\"%s\".*vi5" "fab"} } */
+/* { dg-final { scan-tree-dump "vi5.*vprintf.*\"%c\".*vi6" "fab"} } */
+/* { dg-final { scan-tree-dump "vi6.*vprintf.*\"%s\\\\n\".*vi7" "fab"} } */
--- gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-chk-1.c.jj	2004-09-20 18:09:25.303562733 +0200
+++ gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-chk-1.c	2004-09-20 18:09:25.303562733 +0200
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-fab" } */
+
+typedef struct { int i; } FILE;
+FILE *fp;
+extern int __fprintf_chk (FILE *, int, const char *, ...);
+volatile int vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7, vi8, vi9;
+
+void test (void)
+{
+  vi0 = 0;
+  vi1 = __fprintf_chk (fp, 1, "hello");
+  vi2 = __fprintf_chk (fp, 1, "hello\n");
+  vi3 = __fprintf_chk (fp, 1, "a");
+  vi4 = __fprintf_chk (fp, 1, "");
+  vi5 = __fprintf_chk (fp, 1, "%s", "hello");
+  vi6 = __fprintf_chk (fp, 1, "%s", "hello\n");
+  vi7 = __fprintf_chk (fp, 1, "%s", "a");
+  vi8 = __fprintf_chk (fp, 1, "%c", 'x');
+  vi9 = __fprintf_chk (fp, 1, "%d%d", vi0, vi1);
+}
+
+/* { dg-final { scan-tree-dump "vi0.*fputs.*\"hello\".*fp.*vi1" "fab"} } */
+/* { dg-final { scan-tree-dump "vi1.*fputs.*\"hello\\\\n\".*fp.*vi2" "fab"} } */
+/* { dg-final { scan-tree-dump "vi2.*fputs.*\"a\".*fp.*vi3" "fab"} } */
+/* { dg-final { scan-tree-dump "vi4 = 0" "fab"} } */
+/* { dg-final { scan-tree-dump "vi4.*fputs.*\"hello\".*fp.*vi5" "fab"} } */
+/* { dg-final { scan-tree-dump "vi5.*fputs.*\"hello\\\\n\".*fp.*vi6" "fab"} } */
+/* { dg-final { scan-tree-dump "vi6.*fputs.*\"a\".*fp.*vi7" "fab"} } */
+/* { dg-final { scan-tree-dump "vi7.*fputc.*fp.*vi8" "fab"} } */
+/* { dg-final { scan-tree-dump "vi8.*__fprintf_chk.*fp.*1.*\"%d%d\".*vi9" "fab"} } */
--- gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-chk-1.c.jj	2004-09-20 18:09:25.307562026 +0200
+++ gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-chk-1.c	2004-09-20 18:09:25.307562026 +0200
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-fab" } */
+
+#include <stdarg.h>
+
+extern int __vprintf_chk (int, const char *, va_list);
+volatile int vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7, vi8, vi9, via;
+
+void test (va_list ap)
+{
+  vi0 = 0;
+  vi1 = __vprintf_chk (1, "hello", ap);
+  vi2 = __vprintf_chk (1, "hello\n", ap);
+  vi3 = __vprintf_chk (1, "a", ap);
+  vi4 = __vprintf_chk (1, "", ap);
+  vi5 = __vprintf_chk (1, "%s", ap);
+  vi6 = __vprintf_chk (1, "%c", ap);
+  vi7 = __vprintf_chk (1, "%s\n", ap);
+}
+
+/* { dg-final { scan-tree-dump "vi0.*__vprintf_chk.*1.*\"hello\".*vi1" "fab"} } */
+/* { dg-final { scan-tree-dump "vi1.*puts.*\"hello\".*vi2" "fab"} } */
+/* { dg-final { scan-tree-dump "vi2.*putchar.*vi3" "fab"} } */
+/* { dg-final { scan-tree-dump "vi4 = 0" "fab"} } */
+/* { dg-final { scan-tree-dump "vi4.*__vprintf_chk.*1.*\"%s\".*vi5" "fab"} } */
+/* { dg-final { scan-tree-dump "vi5.*__vprintf_chk.*1.*\"%c\".*vi6" "fab"} } */
+/* { dg-final { scan-tree-dump "vi6.*__vprintf_chk.*1.*\"%s\\\\n\".*vi7" "fab"} } */
--- gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c.jj	2004-09-20 18:09:25.311561318 +0200
+++ gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c	2004-09-20 18:09:25.311561318 +0200
@@ -0,0 +1,65 @@
+/* Test whether buffer overflow warnings for __*_chk builtins
+   are emitted properly.  */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern void abort (void);
+
+#include "../gcc.c-torture/execute/builtins/chk.h"
+#include <stdarg.h>
+
+volatile void *vx;
+char buf1[20];
+int x;
+
+void
+test (int arg, ...)
+{
+  char buf2[20];
+  va_list ap;
+
+  memcpy (&buf2[19], "ab", 1);
+  memcpy (&buf2[19], "ab", 2); /* { dg-warning "will always overflow" "memcpy" } */
+  vx = mempcpy (&buf2[19], "ab", 1);
+  vx = mempcpy (&buf2[19], "ab", 2); /* { dg-warning "will always overflow" "mempcpy" } */
+  memmove (&buf2[18], &buf1[10], 2);
+  memmove (&buf2[18], &buf1[10], 3); /* { dg-warning "will always overflow" "memmove" } */
+  memset (&buf2[16], 'a', 4);
+  memset (&buf2[15], 'b', 6); /* { dg-warning "will always overflow" "memset" } */
+  strcpy (&buf2[18], "a");
+  strcpy (&buf2[18], "ab"); /* { dg-warning "will always overflow" "strcpy" } */
+  vx = stpcpy (&buf2[18], "a");
+  vx = stpcpy (&buf2[18], "ab"); /* { dg-warning "will always overflow" "stpcpy" } */
+  strncpy (&buf2[18], "a", 2);
+  strncpy (&buf2[18], "a", 3); /* { dg-warning "will always overflow" "strncpy" } */
+  strncpy (&buf2[18], "abc", 2);
+  strncpy (&buf2[18], "abc", 3); /* { dg-warning "will always overflow" "strncpy" } */
+  memset (buf2, '\0', sizeof (buf2));
+  strcat (&buf2[18], "a");
+  memset (buf2, '\0', sizeof (buf2));
+  strcat (&buf2[18], "ab"); /* { dg-warning "will always overflow" "strcat" } */
+  sprintf (&buf2[18], "%s", buf1);
+  sprintf (&buf2[18], "%s", "a");
+  sprintf (&buf2[18], "%s", "ab"); /* { dg-warning "will always overflow" "sprintf" } */
+  sprintf (&buf2[18], "a");
+  sprintf (&buf2[18], "ab"); /* { dg-warning "will always overflow" "sprintf" } */
+  snprintf (&buf2[18], 2, "%d", x);
+  /* N argument to snprintf is the size of the buffer.
+     Although this particular call wouldn't overflow buf2,
+     incorrect buffer size was passed to it and therefore
+     we want a warning and runtime failure.  */
+  snprintf (&buf2[18], 3, "%d", x); /* { dg-warning "will always overflow" "snprintf" } */
+  va_start (ap, arg);
+  vsprintf (&buf2[18], "a", ap);
+  va_end (ap);
+  va_start (ap, arg);
+  vsprintf (&buf2[18], "ab", ap); /* { dg-warning "will always overflow" "vsprintf" } */
+  va_end (ap);
+  va_start (ap, arg);
+  vsnprintf (&buf2[18], 2, "%s", ap);
+  va_end (ap);
+  va_start (ap, arg);
+  /* See snprintf above.  */
+  vsnprintf (&buf2[18], 3, "%s", ap); /* { dg-warning "will always overflow" "vsnprintf" } */
+  va_end (ap);
+}
--- gcc/testsuite/gcc.dg/builtin-object-size-1.c.jj	2004-09-20 18:09:25.315560610 +0200
+++ gcc/testsuite/gcc.dg/builtin-object-size-1.c	2004-09-20 18:09:25.315560610 +0200
@@ -0,0 +1,304 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+typedef __SIZE_TYPE__ size_t;
+extern void abort (void);
+extern void exit (int);
+extern void *malloc (size_t);
+extern void *calloc (size_t, size_t);
+extern void *alloca (size_t);
+extern void *memcpy (void *, const void *, size_t);
+extern void *memset (void *, int, size_t);
+extern char *strcpy (char *, const char *);
+
+struct A
+{
+  char a[10];
+  int b;
+  char c[10];
+} y, w[4];
+
+extern char exta[];
+extern char extb[30];
+extern struct A zerol[0];
+
+void
+__attribute__ ((noinline))
+foo (void *q, int x)
+{
+  struct A a;
+  void *p = &a.a[3], *r;
+  char var[x + 10];
+  if (x < 0)
+    r = &a.a[9];
+  else
+    r = &a.c[1];
+  if (__builtin_object_size (p, 0)
+      != sizeof (a) - __builtin_offsetof (struct A, a) - 3)
+    abort ();
+  if (__builtin_object_size (&a.c[9], 0)
+      != sizeof (a) - __builtin_offsetof (struct A, c) - 9)
+    abort ();
+  if (__builtin_object_size (q, 0) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (r, 0)
+      != sizeof (a) - __builtin_offsetof (struct A, a) - 9)
+    abort ();
+  if (x < 6)
+    r = &w[2].a[1];
+  else
+    r = &a.a[6];
+  if (__builtin_object_size (&y, 0)
+      != sizeof (y))
+    abort ();
+  if (__builtin_object_size (w, 0)
+      != sizeof (w))
+    abort ();
+  if (__builtin_object_size (&y.b, 0)
+      != sizeof (a) - __builtin_offsetof (struct A, b))
+    abort ();
+  if (__builtin_object_size (r, 0)
+      != 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1)
+    abort ();
+  if (x < 20)
+    r = malloc (30);
+  else
+    r = calloc (2, 16);
+  if (__builtin_object_size (r, 0) != 2 * 16)
+    abort ();
+  if (x < 20)
+    r = malloc (30);
+  else
+    r = calloc (2, 14);
+  if (__builtin_object_size (r, 0) != 30)
+    abort ();
+  if (x < 30)
+    r = malloc (sizeof (a));
+  else
+    r = &a.a[3];
+  if (__builtin_object_size (r, 0) != sizeof (a))
+    abort ();
+  r = memcpy (r, "a", 2);
+  if (__builtin_object_size (r, 0) != sizeof (a))
+    abort ();
+  r = memcpy (r + 2, "b", 2) + 2;
+  if (__builtin_object_size (r, 0) != sizeof (a) - 4)
+    abort ();
+  r = &a.a[4];
+  r = memset (r, 'a', 2);
+  if (__builtin_object_size (r, 0)
+      != sizeof (a) - __builtin_offsetof (struct A, a) - 4)
+    abort ();
+  r = memset (r + 2, 'b', 2) + 2;
+  if (__builtin_object_size (r, 0)
+      != sizeof (a) - __builtin_offsetof (struct A, a) - 8)
+    abort ();
+  r = &a.a[1];
+  r = strcpy (r, "ab");
+  if (__builtin_object_size (r, 0)
+      != sizeof (a) - __builtin_offsetof (struct A, a) - 1)
+    abort ();
+  r = strcpy (r + 2, "cd") + 2;
+  if (__builtin_object_size (r, 0)
+      != sizeof (a) - __builtin_offsetof (struct A, a) - 5)
+    abort ();
+  if (__builtin_object_size (exta, 0) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (exta + 10, 0) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (&exta[5], 0) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (extb, 0) != sizeof (extb))
+    abort ();
+  if (__builtin_object_size (extb + 10, 0) != sizeof (extb) - 10)
+    abort ();
+  if (__builtin_object_size (&extb[5], 0) != sizeof (extb) - 5)
+    abort ();
+  if (__builtin_object_size (var, 0) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (var + 10, 0) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (&var[5], 0) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (zerol, 0) != 0)
+    abort ();
+  if (__builtin_object_size (&zerol, 0) != 0)
+    abort ();
+  if (__builtin_object_size (&zerol[0], 0) != 0)
+    abort ();
+  if (__builtin_object_size (zerol[0].a, 0) != 0)
+    abort ();
+  if (__builtin_object_size (&zerol[0].a[0], 0) != 0)
+    abort ();
+  if (__builtin_object_size (&zerol[0].b, 0) != (size_t) -1)
+    abort ();
+}
+
+size_t l1 = 1;
+
+void
+__attribute__ ((noinline))
+bar (void)
+{
+  struct B { char buf1[10]; char buf2[10]; } a;
+  char *r, buf3[20];
+  int i;
+
+  if (sizeof (a) != 20)
+    return;
+
+  r = buf3;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        r = &a.buf1[1];
+      else if (i == l1)
+        r = &a.buf2[7];
+      else if (i == l1 + 1)
+        r = &buf3[5];
+      else if (i == l1 + 2)
+        r = &a.buf1[9];
+    }
+  if (__builtin_object_size (r, 0) != 20)
+    abort ();
+  r = &buf3[20];
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        r = &a.buf1[7];
+      else if (i == l1)
+        r = &a.buf2[7];
+      else if (i == l1 + 1)
+        r = &buf3[5];
+      else if (i == l1 + 2)
+        r = &a.buf1[9];
+    }
+  if (__builtin_object_size (r, 0) != 15)
+    abort ();
+  r += 8;
+  if (__builtin_object_size (r, 0) != 7)
+    abort ();
+  if (__builtin_object_size (r + 6, 0) != 1)
+    abort ();
+}
+
+void
+__attribute__ ((noinline))
+baz (void)
+{
+  char buf4[10];
+  struct B { struct A a[2]; struct A b; char c[4]; char d; double e;
+             _Complex double f; } x;
+  double y;
+  _Complex double z;
+  double *dp;
+
+  if (__builtin_object_size (buf4, 0) != sizeof (buf4))
+    abort ();
+  if (__builtin_object_size (&buf4, 0) != sizeof (buf4))
+    abort ();
+  if (__builtin_object_size (&buf4[0], 0) != sizeof (buf4))
+    abort ();
+  if (__builtin_object_size (&buf4[1], 0) != sizeof (buf4) - 1)
+    abort ();
+  if (__builtin_object_size (&x, 0) != sizeof (x))
+    abort ();
+  if (__builtin_object_size (&x.a, 0) != sizeof (x))
+    abort ();
+  if (__builtin_object_size (&x.a[0], 0) != sizeof (x))
+    abort ();
+  if (__builtin_object_size (&x.a[0].a, 0) != sizeof (x))
+    abort ();
+  if (__builtin_object_size (&x.a[0].a[0], 0) != sizeof (x))
+    abort ();
+  if (__builtin_object_size (&x.a[0].a[3], 0) != sizeof (x) - 3)
+    abort ();
+  if (__builtin_object_size (&x.a[0].b, 0)
+      != sizeof (x) - __builtin_offsetof (struct A, b))
+    abort ();
+  if (__builtin_object_size (&x.a[1].c, 0)
+      != sizeof (x) - sizeof (struct A) - __builtin_offsetof (struct A, c))
+    abort ();
+  if (__builtin_object_size (&x.a[1].c[0], 0)
+      != sizeof (x) - sizeof (struct A) - __builtin_offsetof (struct A, c))
+    abort ();
+  if (__builtin_object_size (&x.a[1].c[3], 0)
+      != sizeof (x) - sizeof (struct A) - __builtin_offsetof (struct A, c) - 3)
+    abort ();
+  if (__builtin_object_size (&x.b, 0)
+      != sizeof (x) - __builtin_offsetof (struct B, b))
+    abort ();
+  if (__builtin_object_size (&x.b.a, 0)
+      != sizeof (x) - __builtin_offsetof (struct B, b))
+    abort ();
+  if (__builtin_object_size (&x.b.a[0], 0)
+      != sizeof (x) - __builtin_offsetof (struct B, b))
+    abort ();
+  if (__builtin_object_size (&x.b.a[3], 0)
+      != sizeof (x) - __builtin_offsetof (struct B, b) - 3)
+    abort ();
+  if (__builtin_object_size (&x.b.b, 0)
+      != sizeof (x) - __builtin_offsetof (struct B, b)
+	 - __builtin_offsetof (struct A, b))
+    abort ();
+  if (__builtin_object_size (&x.b.c, 0)
+      != sizeof (x) - __builtin_offsetof (struct B, b)
+	 - __builtin_offsetof (struct A, c))
+    abort ();
+  if (__builtin_object_size (&x.b.c[0], 0)
+      != sizeof (x) - __builtin_offsetof (struct B, b)
+	 - __builtin_offsetof (struct A, c))
+    abort ();
+  if (__builtin_object_size (&x.b.c[3], 0)
+      != sizeof (x) - __builtin_offsetof (struct B, b)
+	 - __builtin_offsetof (struct A, c) - 3)
+    abort ();
+  if (__builtin_object_size (&x.c, 0)
+      != sizeof (x) - __builtin_offsetof (struct B, c))
+    abort ();
+  if (__builtin_object_size (&x.c[0], 0)
+      != sizeof (x) - __builtin_offsetof (struct B, c))
+    abort ();
+  if (__builtin_object_size (&x.c[1], 0)
+      != sizeof (x) - __builtin_offsetof (struct B, c) - 1)
+    abort ();
+  if (__builtin_object_size (&x.d, 0)
+      != sizeof (x) - __builtin_offsetof (struct B, d))
+    abort ();
+  if (__builtin_object_size (&x.e, 0)
+      != sizeof (x) - __builtin_offsetof (struct B, e))
+    abort ();
+  if (__builtin_object_size (&x.f, 0)
+      != sizeof (x) - __builtin_offsetof (struct B, f))
+    abort ();
+  dp = &__real__ x.f;
+  if (__builtin_object_size (dp, 0)
+      != sizeof (x) - __builtin_offsetof (struct B, f))
+    abort ();
+  dp = &__imag__ x.f;
+  if (__builtin_object_size (dp, 0)
+      != sizeof (x) - __builtin_offsetof (struct B, f)
+	 - sizeof (x.f) / 2)
+    abort ();
+  dp = &y;
+  if (__builtin_object_size (dp, 0) != sizeof (y))
+    abort ();
+  if (__builtin_object_size (&z, 0) != sizeof (z))
+    abort ();
+  dp = &__real__ z;
+  if (__builtin_object_size (dp, 0) != sizeof (z))
+    abort ();
+  dp = &__imag__ z;
+  if (__builtin_object_size (dp, 0) != sizeof (z) / 2)
+    abort ();
+}
+
+int
+main (void)
+{
+  __asm ("" : "=r" (l1) : "0" (l1));
+  foo (main, 6);
+  bar ();
+  baz ();
+  exit (0);
+}
--- gcc/testsuite/gcc.dg/builtin-object-size-2.c.jj	2004-09-20 18:09:25.297563795 +0200
+++ gcc/testsuite/gcc.dg/builtin-object-size-2.c	2004-09-20 18:09:25.297563795 +0200
@@ -0,0 +1,299 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+typedef __SIZE_TYPE__ size_t;
+extern void abort (void);
+extern void exit (int);
+extern void *malloc (size_t);
+extern void *calloc (size_t, size_t);
+extern void *alloca (size_t);
+extern void *memcpy (void *, const void *, size_t);
+extern void *memset (void *, int, size_t);
+extern char *strcpy (char *, const char *);
+
+struct A
+{
+  char a[10];
+  int b;
+  char c[10];
+} y, w[4];
+
+extern char exta[];
+extern char extb[30];
+extern struct A extc[];
+struct A zerol[0];
+
+void
+__attribute__ ((noinline))
+foo (void *q, int x)
+{
+  struct A a;
+  void *p = &a.a[3], *r;
+  char var[x + 10];
+  struct A vara[x + 10];
+  if (x < 0)
+    r = &a.a[9];
+  else
+    r = &a.c[1];
+  if (__builtin_object_size (p, 1) != sizeof (a.a) - 3)
+    abort ();
+  if (__builtin_object_size (&a.c[9], 1)
+      != sizeof (a.c) - 9)
+    abort ();
+  if (__builtin_object_size (q, 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (r, 1) != sizeof (a.c) - 1)
+    abort ();
+  if (x < 6)
+    r = &w[2].a[1];
+  else
+    r = &a.a[6];
+  if (__builtin_object_size (&y, 1) != sizeof (y))
+    abort ();
+  if (__builtin_object_size (w, 1) != sizeof (w))
+    abort ();
+  if (__builtin_object_size (&y.b, 1) != sizeof (a.b))
+    abort ();
+  if (__builtin_object_size (r, 1) != sizeof (a.a) - 1)
+    abort ();
+  if (x < 20)
+    r = malloc (30);
+  else
+    r = calloc (2, 16);
+  if (__builtin_object_size (r, 1) != 2 * 16)
+    abort ();
+  if (x < 20)
+    r = malloc (30);
+  else
+    r = calloc (2, 14);
+  if (__builtin_object_size (r, 1) != 30)
+    abort ();
+  if (x < 30)
+    r = malloc (sizeof (a));
+  else
+    r = &a.a[3];
+  if (__builtin_object_size (r, 1) != sizeof (a))
+    abort ();
+  r = memcpy (r, "a", 2);
+  if (__builtin_object_size (r, 1) != sizeof (a))
+    abort ();
+  r = memcpy (r + 2, "b", 2) + 2;
+  if (__builtin_object_size (r, 1) != sizeof (a) - 4)
+    abort ();
+  r = &a.a[4];
+  r = memset (r, 'a', 2);
+  if (__builtin_object_size (r, 1) != sizeof (a.a) - 4)
+    abort ();
+  r = memset (r + 2, 'b', 2) + 2;
+  if (__builtin_object_size (r, 1) != sizeof (a.a) - 8)
+    abort ();
+  r = &a.a[1];
+  r = strcpy (r, "ab");
+  if (__builtin_object_size (r, 1) != sizeof (a.a) - 1)
+    abort ();
+  r = strcpy (r + 2, "cd") + 2;
+  if (__builtin_object_size (r, 1) != sizeof (a.a) - 5)
+    abort ();
+  if (__builtin_object_size (exta, 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (exta + 10, 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (&exta[5], 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (extb, 1) != sizeof (extb))
+    abort ();
+  if (__builtin_object_size (extb + 10, 1) != sizeof (extb) - 10)
+    abort ();
+  if (__builtin_object_size (&extb[5], 1) != sizeof (extb) - 5)
+    abort ();
+  if (__builtin_object_size (extc, 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (extc + 10, 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (&extc[5], 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (&extc->a, 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (&(extc + 10)->b, 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (&extc[5].c[3], 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (var, 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (var + 10, 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (&var[5], 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (vara, 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (vara + 10, 1) != (size_t) -1)
+    abort ();    
+  if (__builtin_object_size (&vara[5], 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (&vara[0].a, 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (&vara[10].a[0], 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (&vara[5].a[4], 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (&vara[5].b, 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (&vara[7].c[7], 1) != (size_t) -1)
+    abort ();
+  if (__builtin_object_size (zerol, 1) != 0)
+    abort ();
+  if (__builtin_object_size (&zerol, 1) != 0)
+    abort ();
+  if (__builtin_object_size (&zerol[0], 1) != 0)
+    abort ();
+  if (__builtin_object_size (zerol[0].a, 1) != 0)
+    abort ();
+  if (__builtin_object_size (&zerol[0].a[0], 1) != 0)
+    abort ();
+  if (__builtin_object_size (&zerol[0].b, 1) != (size_t) -1)
+    abort ();
+}
+
+size_t l1 = 1;
+
+void
+__attribute__ ((noinline))
+bar (void)
+{
+  struct B { char buf1[10]; char buf2[10]; } a;
+  char *r, buf3[20];
+  int i;
+
+  if (sizeof (a) != 20)
+    return;
+
+  r = buf3;
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        r = &a.buf1[1];
+      else if (i == l1)
+        r = &a.buf2[7];
+      else if (i == l1 + 1)
+        r = &buf3[5];
+      else if (i == l1 + 2)
+        r = &a.buf1[9];
+    }
+  if (__builtin_object_size (r, 1) != sizeof (buf3))
+    abort ();
+  r = &buf3[20];
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        r = &a.buf1[7];
+      else if (i == l1)
+        r = &a.buf2[7];
+      else if (i == l1 + 1)
+        r = &buf3[5];
+      else if (i == l1 + 2)
+        r = &a.buf1[9];
+    }
+  if (__builtin_object_size (r, 1) != sizeof (buf3) - 5)
+    abort ();
+  r += 8;
+  if (__builtin_object_size (r, 1) != sizeof (buf3) - 13)
+    abort ();
+  if (__builtin_object_size (r + 6, 1) != sizeof (buf3) - 19)
+    abort ();
+}
+
+void
+__attribute__ ((noinline))
+baz (void)
+{
+  char buf4[10];
+  struct B { struct A a[2]; struct A b; char c[4]; char d; double e;
+             _Complex double f; } x;
+  double y;
+  _Complex double z;
+  double *dp;
+
+  if (__builtin_object_size (buf4, 1) != sizeof (buf4))
+    abort ();
+  if (__builtin_object_size (&buf4, 1) != sizeof (buf4))
+    abort ();
+  if (__builtin_object_size (&buf4[0], 1) != sizeof (buf4))
+    abort ();
+  if (__builtin_object_size (&buf4[1], 1) != sizeof (buf4) - 1)
+    abort ();
+  if (__builtin_object_size (&x, 1) != sizeof (x))
+    abort ();
+  if (__builtin_object_size (&x.a, 1) != sizeof (x.a))
+    abort ();
+  if (__builtin_object_size (&x.a[0], 1) != sizeof (x.a))
+    abort ();
+  if (__builtin_object_size (&x.a[0].a, 1) != sizeof (x.a[0].a))
+    abort ();
+  if (__builtin_object_size (&x.a[0].a[0], 1) != sizeof (x.a[0].a))
+    abort ();
+  if (__builtin_object_size (&x.a[0].a[3], 1) != sizeof (x.a[0].a) - 3)
+    abort ();
+  if (__builtin_object_size (&x.a[0].b, 1) != sizeof (x.a[0].b))
+    abort ();
+  if (__builtin_object_size (&x.a[1].c, 1) != sizeof (x.a[1].c))
+    abort ();
+  if (__builtin_object_size (&x.a[1].c[0], 1) != sizeof (x.a[1].c))
+    abort ();
+  if (__builtin_object_size (&x.a[1].c[3], 1) != sizeof (x.a[1].c) - 3)
+    abort ();
+  if (__builtin_object_size (&x.b, 1) != sizeof (x.b))
+    abort ();
+  if (__builtin_object_size (&x.b.a, 1) != sizeof (x.b.a))
+    abort ();
+  if (__builtin_object_size (&x.b.a[0], 1) != sizeof (x.b.a))
+    abort ();
+  if (__builtin_object_size (&x.b.a[3], 1) != sizeof (x.b.a) - 3)
+    abort ();
+  if (__builtin_object_size (&x.b.b, 1) != sizeof (x.b.b))
+    abort ();
+  if (__builtin_object_size (&x.b.c, 1) != sizeof (x.b.c))
+    abort ();
+  if (__builtin_object_size (&x.b.c[0], 1) != sizeof (x.b.c))
+    abort ();
+  if (__builtin_object_size (&x.b.c[3], 1) != sizeof (x.b.c) - 3)
+    abort ();
+  if (__builtin_object_size (&x.c, 1) != sizeof (x.c))
+    abort ();
+  if (__builtin_object_size (&x.c[0], 1) != sizeof (x.c))
+    abort ();
+  if (__builtin_object_size (&x.c[1], 1) != sizeof (x.c) - 1)
+    abort ();
+  if (__builtin_object_size (&x.d, 1) != sizeof (x.d))
+    abort ();
+  if (__builtin_object_size (&x.e, 1) != sizeof (x.e))
+    abort ();
+  if (__builtin_object_size (&x.f, 1) != sizeof (x.f))
+    abort ();
+  dp = &__real__ x.f;
+  if (__builtin_object_size (dp, 1) != sizeof (x.f) / 2)
+    abort ();
+  dp = &__imag__ x.f;
+  if (__builtin_object_size (dp, 1) != sizeof (x.f) / 2)
+    abort ();
+  dp = &y;
+  if (__builtin_object_size (dp, 1) != sizeof (y))
+    abort ();
+  if (__builtin_object_size (&z, 1) != sizeof (z))
+      abort ();
+  dp = &__real__ z;
+  if (__builtin_object_size (dp, 1) != sizeof (z) / 2)
+    abort ();
+  dp = &__imag__ z;
+  if (__builtin_object_size (dp, 1) != sizeof (z) / 2)
+    abort ();
+}
+
+int
+main (void)
+{
+  __asm ("" : "=r" (l1) : "0" (l1));
+  foo (main, 6);
+  bar ();
+  baz ();
+  exit (0);
+}

	Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]