]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
* builtin-attrs.def (DEF_ATTR_FOR_INT): Add for 5 and 6.
authorjakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 27 Jun 2005 12:17:39 +0000 (12:17 +0000)
committerjakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 27 Jun 2005 12:17:39 +0000 (12:17 +0000)
(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): Comment fixes.
(expand_builtin_object_size, 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 (OBJS-common): Add tree-object-size.o.
(tree-object-size.o): Add dependencies.
* tree-pass.h (pass_object_sizes): Add.
* tree-optimize.c (init_tree_optimization_passes): Add
pass_object_sizes.
* tree-object-size.c: New file.
* tree.h (fold_builtin_memory_chk, fold_builtin_stxcpy_chk,
fold_builtin_strncpy_chk, fold_builtin_snprintf_chk,
compute_builtin_object_size, init_object_sizes, fini_object_sizes):
New prototypes.
* 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.  Pass CALLEE and ARGLIST variables to the
folding functions instead of computing them again.
(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-object-size-3.c: New test.
* gcc.dg/builtin-object-size-4.c: New test.
* gcc.dg/builtin-object-size-5.c: New test.
* gcc.dg/builtin-stringop-chk-1.c: New test.
* gcc.dg/builtin-stringop-chk-2.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.c-torture/execute/printf-1.c: New test.
* gcc.c-torture/execute/fprintf-1.c: New test.
* gcc.c-torture/execute/vprintf-1.c: New test.
* gcc.c-torture/execute/vfprintf-1.c: New test.
* gcc.c-torture/execute/printf-chk-1.c: New test.
* gcc.c-torture/execute/fprintf-chk-1.c: New test.
* gcc.c-torture/execute/vprintf-chk-1.c: New test.
* gcc.c-torture/execute/vfprintf-chk-1.c: New test.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@101352 138bc75d-0d04-0410-961f-82ee72b054a4

67 files changed:
gcc/ChangeLog
gcc/Makefile.in
gcc/builtin-attrs.def
gcc/builtin-types.def
gcc/builtins.c
gcc/builtins.def
gcc/c-common.c
gcc/doc/extend.texi
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.c-torture/execute/builtins/chk.h [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/lib/chk.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/lib/main.c
gcc/testsuite/gcc.c-torture/execute/builtins/lib/strncat.c
gcc/testsuite/gcc.c-torture/execute/builtins/memcpy-chk-lib.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/memcpy-chk.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/memmove-chk-lib.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/memmove-chk.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk-lib.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/memset-chk-lib.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/memset-chk.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/snprintf-chk-lib.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/snprintf-chk.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/sprintf-chk-lib.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/sprintf-chk.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/stpcpy-chk-lib.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/stpcpy-chk.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/strcat-chk-lib.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/strcat-chk.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/strcpy-chk-lib.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/strcpy-chk.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk-lib.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/strncpy-chk-lib.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/strncpy-chk.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/vsnprintf-chk-lib.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/vsnprintf-chk.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/vsprintf-chk-lib.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/vsprintf-chk.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/fprintf-1.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/fprintf-chk-1.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/printf-1.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/printf-chk-1.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/vfprintf-1.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/vfprintf-chk-1.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/vprintf-1.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/vprintf-chk-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtin-object-size-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtin-object-size-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtin-object-size-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtin-object-size-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtin-object-size-5.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtin-stringop-chk-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-chk-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-chk-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-chk-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-chk-1.c [new file with mode: 0644]
gcc/tree-object-size.c [new file with mode: 0644]
gcc/tree-optimize.c
gcc/tree-pass.h
gcc/tree-ssa-ccp.c
gcc/tree.h

index 092783dfe3aed6bf3255464ba6171092f6e8e48e..2ee61b3fe50275d546a4f7b57c496acf66d18509 100644 (file)
@@ -1,5 +1,64 @@
 2005-06-27  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): Comment fixes.
+       (expand_builtin_object_size, 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 (OBJS-common): Add tree-object-size.o.
+       (tree-object-size.o): Add dependencies.
+       * tree-pass.h (pass_object_sizes): Add.
+       * tree-optimize.c (init_tree_optimization_passes): Add
+       pass_object_sizes.
+       * tree-object-size.c: New file.
+       * tree.h (fold_builtin_memory_chk, fold_builtin_stxcpy_chk,
+       fold_builtin_strncpy_chk, fold_builtin_snprintf_chk,
+       compute_builtin_object_size, init_object_sizes, fini_object_sizes):
+       New prototypes.
+       * 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.  Pass CALLEE and ARGLIST variables to the
+       folding functions instead of computing them again.
+       (execute_fold_all_builtins): Retry ccp_fold_builtin if a builtin changed
+       into some other builtin.
+       * doc/extend.texi (Object Size Checking): Document.
+
        * regrename.c (copy_value): Fix comment.
 
        * toplev.c (process_options): Use if (FRAME_GROWS_DOWNWARD)
index 628d36ca4212018ef9c019c38b2168d11ee5db15..98ace16f4a79b382ad4c76ad5d04728a143b5448 100644 (file)
@@ -956,7 +956,7 @@ OBJS-common = \
  rtl-profile.o tree-profile.o rtlhooks.o cfgexpand.o lambda-mat.o          \
  lambda-trans.o        lambda-code.o tree-loop-linear.o tree-ssa-sink.o           \
  tree-vrp.o tree-stdarg.o tree-cfgcleanup.o tree-ssa-reassoc.o            \
- tree-ssa-structalias.o
+ tree-ssa-structalias.o tree-object-size.o
 
 
 OBJS-md = $(out_object_file)
@@ -1945,6 +1945,9 @@ tree-loop-linear.o: tree-loop-linear.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
 tree-stdarg.o: tree-stdarg.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
    $(TREE_H) function.h $(DIAGNOSTIC_H) $(TREE_FLOW_H) tree-pass.h \
    tree-stdarg.h $(TARGET_H) langhooks.h
+tree-object-size.o: tree-object-size.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
+   $(TM_H) $(TREE_H) $(DIAGNOSTIC_H) $(TREE_FLOW_H) tree-pass.h \
+   tree-ssa-propagate.h
 tree-gimple.o : tree-gimple.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(EXPR_H) \
    $(RTL_H) $(TREE_GIMPLE_H) $(TM_H) coretypes.h bitmap.h $(GGC_H) \
    output.h $(TREE_FLOW_H)
index 34bde745026a0c229106ec75615bf32a53876a4d..46ac70d4c35ba799926822608295a2900558ae36 100644 (file)
@@ -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.  */
@@ -127,6 +133,12 @@ DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_2, ATTR_NONNULL, ATTR_LIST_2, \
 /* Nothrow functions whose third parameter is a nonnull pointer.  */
 DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_3, ATTR_NONNULL, ATTR_LIST_3, \
                        ATTR_NOTHROW_LIST)
+/* Nothrow functions whose fourth parameter is a nonnull pointer.  */
+DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_4, ATTR_NONNULL, ATTR_LIST_4, \
+                       ATTR_NOTHROW_LIST)
+/* Nothrow functions whose fifth parameter is a nonnull pointer.  */
+DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_5, ATTR_NONNULL, ATTR_LIST_5, \
+                       ATTR_NOTHROW_LIST)
 /* Nothrow const functions whose pointer parameter(s) are all nonnull.  */
 DEF_ATTR_TREE_LIST (ATTR_CONST_NOTHROW_NONNULL, ATTR_CONST, ATTR_NULL, \
                        ATTR_NOTHROW_NONNULL)
@@ -149,6 +161,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)
index 4638e29bb9d50e21716ce245c99c39025f6fb6fb..3f25cdd447783b0b58d78ce316913f774ba3f426 100644 (file)
@@ -32,6 +32,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
    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, 51 Franklin Street, Fifth Floor, Boston, MA
    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:
@@ -252,6 +254,7 @@ DEF_FUNCTION_TYPE_2 (BT_FN_COMPLEX_LONGDOUBLE_COMPLEX_LONGDOUBLE_COMPLEX_LONGDOU
 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_2 (BT_FN_I1_VPTR_I1, BT_I1, BT_VOLATILE_PTR, BT_I1)
 DEF_FUNCTION_TYPE_2 (BT_FN_I2_VPTR_I2, BT_I2, BT_VOLATILE_PTR, BT_I2)
 DEF_FUNCTION_TYPE_2 (BT_FN_I4_VPTR_I4, BT_I4, BT_VOLATILE_PTR, BT_I4)
@@ -300,6 +303,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_LONGDOUBLE_LONGDOUBLEPTR_LONGDOUBLEPTR,
 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_3 (BT_FN_BOOL_VPTR_I1_I1, BT_BOOL, BT_VOLATILE_PTR,
                     BT_I1, BT_I1)
 DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_VPTR_I2_I2, BT_BOOL, BT_VOLATILE_PTR,
@@ -319,6 +324,22 @@ DEF_FUNCTION_TYPE_4 (BT_FN_INT_STRING_SIZE_CONST_STRING_VALIST_ARG,
                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)
@@ -337,11 +358,22 @@ DEF_FUNCTION_TYPE_VAR_2 (BT_FN_INT_STRING_CONST_STRING_VAR,
                         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,
index c966c0e0da306016b4dbb8b1da10ea85c61687c4..313170bbfbb9f6437a425fe178d05386f732efc2 100644 (file)
@@ -188,6 +188,18 @@ static tree fold_builtin_strspn (tree);
 static tree fold_builtin_strcspn (tree);
 static tree fold_builtin_sprintf (tree, int);
 
+static rtx expand_builtin_object_size (tree);
+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 tree fold_builtin_object_size (tree);
+static tree fold_builtin_strcat_chk (tree, tree);
+static tree fold_builtin_strncat_chk (tree, tree);
+static tree fold_builtin_sprintf_chk (tree, enum built_in_function);
+static tree fold_builtin_printf (tree, tree, bool, enum built_in_function);
+static tree fold_builtin_fprintf (tree, tree, bool, enum built_in_function);
+
 /* Return true if NODE should be considered for inline expansion regardless
    of the optimization level.  This means whenever a function is invoked with
    its "internal" name, which normally contains the prefix "__builtin".  */
@@ -2821,7 +2833,7 @@ expand_builtin_memcpy (tree exp, rtx target, enum machine_mode mode)
 }
 
 /* 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
@@ -2912,7 +2924,7 @@ expand_builtin_mempcpy (tree arglist, tree type, rtx target, enum machine_mode m
 }
 
 /* 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, tree type, rtx target,
@@ -6238,6 +6250,32 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
       expand_builtin_synchronize ();
       return const0_rtx;
 
+    case BUILT_IN_OBJECT_SIZE:
+      return expand_builtin_object_size (exp);
+
+    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;
     }
@@ -8787,6 +8825,48 @@ fold_builtin_1 (tree fndecl, tree arglist, bool ignore)
     case BUILT_IN_VA_START:
       break;
 
+    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 (fndecl, arglist, NULL_TREE, ignore,
+                                     DECL_FUNCTION_CODE (fndecl));
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STPCPY_CHK:
+      return fold_builtin_stxcpy_chk (fndecl, arglist, NULL_TREE, ignore,
+                                     DECL_FUNCTION_CODE (fndecl));
+    case BUILT_IN_STRNCPY_CHK:
+      return fold_builtin_strncpy_chk (arglist, NULL_TREE);
+    case BUILT_IN_STRCAT_CHK:
+      return fold_builtin_strcat_chk (fndecl, arglist);
+    case BUILT_IN_STRNCAT_CHK:
+      return fold_builtin_strncat_chk (fndecl, arglist);
+    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 (fndecl, arglist, ignore,
+                                 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 (fndecl, arglist, ignore,
+                                  DECL_FUNCTION_CODE (fndecl));
+
     default:
       break;
     }
@@ -9238,7 +9318,7 @@ fold_builtin_strncat (tree arglist)
       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'))
         return omit_two_operands (TREE_TYPE (dst), dst, src, len);
 
@@ -9535,7 +9615,7 @@ fold_builtin_sprintf (tree arglist, int ignored)
      'sprintf (dest, "%s", orig)'.  */
   if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)
       && !validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, POINTER_TYPE,
-                           VOID_TYPE))
+                           VOID_TYPE))
     return NULL_TREE;
 
   /* Get the destination string and the format specifier.  */
@@ -9599,3 +9679,1144 @@ fold_builtin_sprintf (tree arglist, int ignored)
   else
     return call;
 }
+
+/* Expand a call to __builtin_object_size.  */
+
+rtx
+expand_builtin_object_size (tree exp)
+{
+  tree ost;
+  int object_size_type;
+  tree fndecl = get_callee_fndecl (exp);
+  tree arglist = TREE_OPERAND (exp, 1);
+  location_t locus = EXPR_LOCATION (exp);
+
+  if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    {
+      error ("%Hfirst argument of %D must be a pointer, second integer constant",
+            &locus, fndecl);
+      expand_builtin_trap ();
+      return const0_rtx;
+    }
+
+  ost = TREE_VALUE (TREE_CHAIN (arglist));
+  STRIP_NOPS (ost);
+
+  if (TREE_CODE (ost) != INTEGER_CST
+      || tree_int_cst_sgn (ost) < 0
+      || compare_tree_int (ost, 3) > 0)
+    {
+      error ("%Hlast argument of %D is not integer constant between 0 and 3",
+            &locus, fndecl);
+      expand_builtin_trap ();
+      return const0_rtx;
+    }
+
+  object_size_type = tree_low_cst (ost, 0);
+
+  return object_size_type < 2 ? constm1_rtx : const0_rtx;
+}
+
+/* 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 (0, "%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;
+
+      fn = build_function_call_expr (fn, arglist);
+      if (TREE_CODE (fn) == CALL_EXPR)
+       CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+      return expand_expr (fn, 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 fn = built_in_decls[BUILT_IN_MEMCPY_CHK];
+             if (!fn)
+               return 0;
+             fn = build_function_call_expr (fn, arglist);
+             if (TREE_CODE (fn) == CALL_EXPR)
+               CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+             return expand_expr (fn, 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 (0, "%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 (! POINTER_TYPE_P (TREE_TYPE (arg)))
+       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 (0, "%Hcall to %D will always overflow destination buffer",
+              &locus, get_callee_fndecl (exp));
+    }
+}
+
+/* Fold a call to __builtin_object_size, if possible.  */
+
+tree
+fold_builtin_object_size (tree arglist)
+{
+  tree ptr, ost, ret = 0;
+  int object_size_type;
+
+  if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return 0;
+
+  ptr = TREE_VALUE (arglist);
+  ost = TREE_VALUE (TREE_CHAIN (arglist));
+  STRIP_NOPS (ost);
+
+  if (TREE_CODE (ost) != INTEGER_CST
+      || tree_int_cst_sgn (ost) < 0
+      || compare_tree_int (ost, 3) > 0)
+    return 0;
+
+  object_size_type = tree_low_cst (ost, 0);
+
+  /* __builtin_object_size doesn't evaluate side-effects in its arguments;
+     if there are any side-effects, it returns (size_t) -1 for types 0 and 1
+     and (size_t) 0 for types 2 and 3.  */
+  if (TREE_SIDE_EFFECTS (ptr))
+    return fold_convert (size_type_node,
+                        object_size_type < 2
+                        ? integer_minus_one_node : integer_zero_node);
+
+  if (TREE_CODE (ptr) == ADDR_EXPR)
+    ret = build_int_cstu (size_type_node,
+                       compute_builtin_object_size (ptr, object_size_type));
+
+  else if (TREE_CODE (ptr) == SSA_NAME)
+    {
+      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, object_size_type);
+      if (bytes != (unsigned HOST_WIDE_INT) (object_size_type < 2
+                                            ? -1 : 0))
+       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 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_memory_chk (tree fndecl, tree arglist, tree maxlen, bool ignore,
+                        enum built_in_function fcode)
+{
+  tree dest, src, len, size, fn;
+
+  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 (TREE_TYPE (fndecl)), 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 (TREE_TYPE (fndecl)), 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 fndecl, tree arglist, tree maxlen, bool ignore,
+                        enum built_in_function fcode)
+{
+  tree dest, src, size, len, fn;
+
+  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 (TREE_TYPE (fndecl)), 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 (TREE_TYPE (fndecl)),
+                                  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 arglist, tree maxlen)
+{
+  tree dest, src, size, len, fn;
+
+  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 FNDECL with ARGLIST.  */
+
+static tree
+fold_builtin_strcat_chk (tree fndecl, tree arglist)
+{
+  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 (TREE_TYPE (fndecl)), 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 fndecl, tree arglist)
+{
+  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 (TREE_TYPE (fndecl)), dest, len);
+  else if (integer_zerop (len))
+    return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), 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 (! POINTER_TYPE_P (TREE_TYPE (dest)))
+    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 (! POINTER_TYPE_P (TREE_TYPE (fmt)))
+    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 (POINTER_TYPE_P (TREE_TYPE (arg)))
+               {
+                 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 (! POINTER_TYPE_P (TREE_TYPE (dest)))
+    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 (! POINTER_TYPE_P (TREE_TYPE (fmt)))
+    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 {,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 fndecl, tree arglist, bool ignore,
+                    enum built_in_function fcode)
+{
+  tree fmt, fn = NULL_TREE, fn_putchar, fn_puts, arg, call;
+  const char *fmt_str = NULL;
+
+  /* If the return value is used, don't do the transformation.  */
+  if (! ignore)
+    return 0;
+
+  /* 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 (! POINTER_TYPE_P (TREE_TYPE (fmt)))
+    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
+             || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
+             || 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 (TREE_TYPE (fndecl)), 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
+         || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
+         || 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 (TREE_TYPE (fndecl)), 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 fndecl, tree arglist, bool ignore,
+                     enum built_in_function fcode)
+{
+  tree fp, fmt, fn = NULL_TREE, fn_fputc, fn_fputs, arg, call;
+  const char *fmt_str = NULL;
+
+  /* If the return value is used, don't do the transformation.  */
+  if (! ignore)
+    return 0;
+
+  /* Verify the required arguments in the original call.  */
+  if (! arglist)
+    return 0;
+  fp = TREE_VALUE (arglist);
+  if (! POINTER_TYPE_P (TREE_TYPE (fp)))
+    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 (! POINTER_TYPE_P (TREE_TYPE (fmt)))
+    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 (TREE_TYPE (fndecl)), 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
+         || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
+         || 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 (TREE_TYPE (fndecl)), call);
+}
index a5dda5b9a7a18609cd857921a9a338a953ddcc4e..2f5aa151a4f589aced320af9b233dfe0543b0d51 100644 (file)
@@ -658,6 +658,26 @@ DEF_BUILTIN_STUB (BUILT_IN_NONLOCAL_GOTO, "__builtin_nonlocal_goto")
 DEF_BUILTIN_STUB (BUILT_IN_STACK_SAVE, "__builtin_stack_save")
 DEF_BUILTIN_STUB (BUILT_IN_STACK_RESTORE, "__builtin_stack_restore")
 
+/* 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)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMMOVE_CHK, "__memmove_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMPCPY_CHK, "__mempcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMSET_CHK, "__memset_chk", BT_FN_PTR_PTR_INT_SIZE_SIZE, ATTR_NOTHROW_NONNULL)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_STPCPY_CHK, "__stpcpy_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRCAT_CHK, "__strcat_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRCPY_CHK, "__strcpy_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRNCAT_CHK, "__strncat_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_NOTHROW_NONNULL)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRNCPY_CHK, "__strncpy_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_NOTHROW_NONNULL)
+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_BUILTIN_STUB (BUILT_IN_PROFILE_FUNC_ENTER, "profile_func_enter")
 DEF_BUILTIN_STUB (BUILT_IN_PROFILE_FUNC_EXIT, "profile_func_exit")
index 24280aef282fe551c41cca14434ac7b72eb6d711..bcf28090554409bedb079039c3f3ec5659bd8593 100644 (file)
@@ -2924,10 +2924,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
@@ -2936,10 +2942,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
   };
@@ -3188,6 +3198,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);
@@ -3220,6 +3266,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]);
@@ -3229,10 +3307,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 ();
index c0363ddbe0f7665a587454ee4ea4069777e08fba..b019d3882194680943afbb1408f425b04d364ebc 100644 (file)
@@ -71,6 +71,8 @@ extensions, accepted by GCC in C89 mode and in C++.
 * Vector Extensions::   Using vector instructions through built-in functions.
 * Offsetof::            Special syntax for implementing @code{offsetof}.
 * Atomic Builtins::    Built-in functions for atomic memory access.
+* 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.
@@ -4724,6 +4726,139 @@ previous memory loads have been satisfied, but following memory reads
 are not prevented from being speculated to before the barrier.
 @end table
 
+@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{type})
+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} for @var{type} 0 or 1 and @code{(size_t) 0}
+for @var{type} 2 or 3.  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 @var{type} & 2 is
+0 and minimum if non-zero.  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} for @var{type} 0 or 1 and @code{(size_t) 0}
+for @var{type} 2 or 3.
+
+@var{type} is an integer constant from 0 to 3.  If the least significant
+bit is clear, objects are whole variables, if it is set, a closest
+surrounding subobject is considered the object a pointer points to.
+The second bit determines if maximum or minimum of remaining bytes
+is computed.
+
+@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 bos0(dest) __builtin_object_size (dest, 0)
+#define memcpy(dest, src, n) \
+  __builtin___memcpy_chk (dest, src, n, bos0 (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
index c587b1357ed7df697fff6941975eedf98dcdc88b..31444a40c9a39deeabcb894cd2da4a8d2600085b 100644 (file)
@@ -1,3 +1,61 @@
+2005-06-27  Jakub Jelinek  <jakub@redhat.com>
+
+       * 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-object-size-3.c: New test.
+       * gcc.dg/builtin-object-size-4.c: New test.
+       * gcc.dg/builtin-object-size-5.c: New test.
+       * gcc.dg/builtin-stringop-chk-1.c: New test.
+       * gcc.dg/builtin-stringop-chk-2.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.c-torture/execute/printf-1.c: New test.
+       * gcc.c-torture/execute/fprintf-1.c: New test.
+       * gcc.c-torture/execute/vprintf-1.c: New test.
+       * gcc.c-torture/execute/vfprintf-1.c: New test.
+       * gcc.c-torture/execute/printf-chk-1.c: New test.
+       * gcc.c-torture/execute/fprintf-chk-1.c: New test.
+       * gcc.c-torture/execute/vprintf-chk-1.c: New test.
+       * gcc.c-torture/execute/vfprintf-chk-1.c: New test.
+
 2005-06-27  Michael Matz  <matz@suse.de>
 
        * gcc.target/x86_64/abi/test_struct_returning.c: Adjust as return
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/chk.h b/gcc/testsuite/gcc.c-torture/execute/builtins/chk.h
new file mode 100644 (file)
index 0000000..dfef410
--- /dev/null
@@ -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;
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/lib/chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/lib/chk.c
new file mode 100644 (file)
index 0000000..eb305d4
--- /dev/null
@@ -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;
+}
index 1ca606565a8fbe04738c5a4bbc5113ac4905e4c1..a9bb6c6b890ca929b73f715653943eea5b2d746d 100644 (file)
@@ -1,5 +1,6 @@
 extern void abort(void);
 extern void main_test (void);
+extern void abort (void);
 int inside_main;
 
 int
index 051dc46c723ea21728d0a82776fe7d229c563c83..290d4cf49bb472557e94bae1955957be650b84ca 100644 (file)
@@ -13,11 +13,12 @@ strncat (char *s1, const char *s2, size_t n)
     abort();
 #endif
   while (*s1) s1++;
+  c = '\0';
   while (n > 0)
     {
       c = *s2++;
       *s1++ = c;
-      if (c == 0)
+      if (c == '\0')
        return dest;
       n--;
     }
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/memcpy-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/memcpy-chk-lib.c
new file mode 100644 (file)
index 0000000..9daf13e
--- /dev/null
@@ -0,0 +1 @@
+#include "lib/chk.c"
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/memcpy-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/memcpy-chk.c
new file mode 100644 (file)
index 0000000..28f7ae7
--- /dev/null
@@ -0,0 +1,479 @@
+/* Copyright (C) 2004, 2005  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 ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/memmove-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/memmove-chk-lib.c
new file mode 100644 (file)
index 0000000..9daf13e
--- /dev/null
@@ -0,0 +1 @@
+#include "lib/chk.c"
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/memmove-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/memmove-chk.c
new file mode 100644 (file)
index 0000000..1227227
--- /dev/null
@@ -0,0 +1,579 @@
+/* Copyright (C) 2004, 2005  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 ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk-lib.c
new file mode 100644 (file)
index 0000000..9daf13e
--- /dev/null
@@ -0,0 +1 @@
+#include "lib/chk.c"
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk.c
new file mode 100644 (file)
index 0000000..a59d59b
--- /dev/null
@@ -0,0 +1,487 @@
+/* Copyright (C) 2004, 2005  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 ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/memset-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/memset-chk-lib.c
new file mode 100644 (file)
index 0000000..9daf13e
--- /dev/null
@@ -0,0 +1 @@
+#include "lib/chk.c"
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/memset-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/memset-chk.c
new file mode 100644 (file)
index 0000000..a8f09a7
--- /dev/null
@@ -0,0 +1,721 @@
+/* Copyright (C) 2004, 2005  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 ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/snprintf-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/snprintf-chk-lib.c
new file mode 100644 (file)
index 0000000..9daf13e
--- /dev/null
@@ -0,0 +1 @@
+#include "lib/chk.c"
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/snprintf-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/snprintf-chk.c
new file mode 100644 (file)
index 0000000..e6ddc08
--- /dev/null
@@ -0,0 +1,220 @@
+/* Copyright (C) 2004, 2005  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 + 3, 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 ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/sprintf-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/sprintf-chk-lib.c
new file mode 100644 (file)
index 0000000..9daf13e
--- /dev/null
@@ -0,0 +1 @@
+#include "lib/chk.c"
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/sprintf-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/sprintf-chk.c
new file mode 100644 (file)
index 0000000..95d2a9d
--- /dev/null
@@ -0,0 +1,197 @@
+/* Copyright (C) 2004, 2005  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 + 3, 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 ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/stpcpy-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/stpcpy-chk-lib.c
new file mode 100644 (file)
index 0000000..9daf13e
--- /dev/null
@@ -0,0 +1 @@
+#include "lib/chk.c"
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/stpcpy-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/stpcpy-chk.c
new file mode 100644 (file)
index 0000000..b292c0a
--- /dev/null
@@ -0,0 +1,265 @@
+/* Copyright (C) 2004, 2005  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 ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strcat-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strcat-chk-lib.c
new file mode 100644 (file)
index 0000000..9daf13e
--- /dev/null
@@ -0,0 +1 @@
+#include "lib/chk.c"
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strcat-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strcat-chk.c
new file mode 100644 (file)
index 0000000..fea3184
--- /dev/null
@@ -0,0 +1,204 @@
+/* Copyright (C) 2004, 2005  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 ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strcpy-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strcpy-chk-lib.c
new file mode 100644 (file)
index 0000000..9daf13e
--- /dev/null
@@ -0,0 +1 @@
+#include "lib/chk.c"
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strcpy-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strcpy-chk.c
new file mode 100644 (file)
index 0000000..002dd19
--- /dev/null
@@ -0,0 +1,234 @@
+/* Copyright (C) 2004, 2005  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;
+#ifndef __OPTIMIZE_SIZE__
+  strcpy_disallowed = 1;
+#else
+  strcpy_disallowed = 0;
+#endif
+
+  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 ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk-lib.c
new file mode 100644 (file)
index 0000000..9daf13e
--- /dev/null
@@ -0,0 +1 @@
+#include "lib/chk.c"
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk.c
new file mode 100644 (file)
index 0000000..8904df1
--- /dev/null
@@ -0,0 +1,229 @@
+/* Copyright (C) 2004, 2005  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 ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strncpy-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strncpy-chk-lib.c
new file mode 100644 (file)
index 0000000..9daf13e
--- /dev/null
@@ -0,0 +1 @@
+#include "lib/chk.c"
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strncpy-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strncpy-chk.c
new file mode 100644 (file)
index 0000000..46f3374
--- /dev/null
@@ -0,0 +1,227 @@
+/* Copyright (C) 2004, 2005  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 ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/vsnprintf-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/vsnprintf-chk-lib.c
new file mode 100644 (file)
index 0000000..9daf13e
--- /dev/null
@@ -0,0 +1 @@
+#include "lib/chk.c"
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/vsnprintf-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/vsnprintf-chk.c
new file mode 100644 (file)
index 0000000..8c7d72f
--- /dev/null
@@ -0,0 +1,321 @@
+/* Copyright (C) 2004, 2005  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 + 3, 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 ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/vsprintf-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/vsprintf-chk-lib.c
new file mode 100644 (file)
index 0000000..9daf13e
--- /dev/null
@@ -0,0 +1 @@
+#include "lib/chk.c"
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/vsprintf-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/vsprintf-chk.c
new file mode 100644 (file)
index 0000000..5c15090
--- /dev/null
@@ -0,0 +1,290 @@
+/* Copyright (C) 2004, 2005  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 + 3, 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 ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/fprintf-1.c b/gcc/testsuite/gcc.c-torture/execute/fprintf-1.c
new file mode 100644 (file)
index 0000000..f16252b
--- /dev/null
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+#define test(ret, args...) \
+  fprintf (stdout, args);              \
+  if (fprintf (stdout, args) != ret)   \
+    abort ();
+  test (5, "hello");
+  test (6, "hello\n");
+  test (1, "a");
+  test (0, "");
+  test (5, "%s", "hello");
+  test (6, "%s", "hello\n");
+  test (1, "%s", "a");
+  test (0, "%s", "");
+  test (1, "%c", 'x');
+  test (7, "%s\n", "hello\n");
+  test (2, "%d\n", 0);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/fprintf-chk-1.c b/gcc/testsuite/gcc.c-torture/execute/fprintf-chk-1.c
new file mode 100644 (file)
index 0000000..918ff8e
--- /dev/null
@@ -0,0 +1,49 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+volatile int should_optimize;
+
+int
+__attribute__((noinline))
+__fprintf_chk (FILE *f, int flag, const char *fmt, ...)
+{
+  va_list ap;
+  int ret;
+#ifdef __OPTIMIZE__
+  if (should_optimize)
+    abort ();
+#endif
+  should_optimize = 1;
+  va_start (ap, fmt);
+  ret = vfprintf (f, fmt, ap);
+  va_end (ap);
+  return ret;
+}
+
+int
+main (void)
+{
+#define test(ret, opt, args...) \
+  should_optimize = opt;                       \
+  __fprintf_chk (stdout, 1, args);             \
+  if (!should_optimize)                                \
+    abort ();                                  \
+  should_optimize = 0;                         \
+  if (__fprintf_chk (stdout, 1, args) != ret)  \
+    abort ();                                  \
+  if (!should_optimize)                                \
+    abort ();
+  test (5, 1, "hello");
+  test (6, 1, "hello\n");
+  test (1, 1, "a");
+  test (0, 1, "");
+  test (5, 1, "%s", "hello");
+  test (6, 1, "%s", "hello\n");
+  test (1, 1, "%s", "a");
+  test (0, 1, "%s", "");
+  test (1, 1, "%c", 'x');
+  test (7, 0, "%s\n", "hello\n");
+  test (2, 0, "%d\n", 0);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/printf-1.c b/gcc/testsuite/gcc.c-torture/execute/printf-1.c
new file mode 100644 (file)
index 0000000..0ffcd5d
--- /dev/null
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+#define test(ret, args...) \
+  printf (args);               \
+  if (printf (args) != ret)    \
+    abort ();
+  test (5, "hello");
+  test (6, "hello\n");
+  test (1, "a");
+  test (0, "");
+  test (5, "%s", "hello");
+  test (6, "%s", "hello\n");
+  test (1, "%s", "a");
+  test (0, "%s", "");
+  test (1, "%c", 'x');
+  test (7, "%s\n", "hello\n");
+  test (2, "%d\n", 0);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/printf-chk-1.c b/gcc/testsuite/gcc.c-torture/execute/printf-chk-1.c
new file mode 100644 (file)
index 0000000..8f9a79c
--- /dev/null
@@ -0,0 +1,49 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+volatile int should_optimize;
+
+int
+__attribute__((noinline))
+__printf_chk (int flag, const char *fmt, ...)
+{
+  va_list ap;
+  int ret;
+#ifdef __OPTIMIZE__
+  if (should_optimize)
+    abort ();
+#endif
+  should_optimize = 1;
+  va_start (ap, fmt);
+  ret = vprintf (fmt, ap);
+  va_end (ap);
+  return ret;
+}
+
+int
+main (void)
+{
+#define test(ret, opt, args...) \
+  should_optimize = opt;               \
+  __printf_chk (1, args);              \
+  if (!should_optimize)                        \
+    abort ();                          \
+  should_optimize = 0;                 \
+  if (__printf_chk (1, args) != ret)   \
+    abort ();                          \
+  if (!should_optimize)                        \
+    abort ();
+  test (5, 0, "hello");
+  test (6, 1, "hello\n");
+  test (1, 1, "a");
+  test (0, 1, "");
+  test (5, 0, "%s", "hello");
+  test (6, 1, "%s", "hello\n");
+  test (1, 1, "%s", "a");
+  test (0, 1, "%s", "");
+  test (1, 1, "%c", 'x');
+  test (7, 1, "%s\n", "hello\n");
+  test (2, 0, "%d\n", 0);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/vfprintf-1.c b/gcc/testsuite/gcc.c-torture/execute/vfprintf-1.c
new file mode 100644 (file)
index 0000000..c003804
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef test
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+void
+inner (int x, ...)
+{
+  va_list ap, ap2;
+  va_start (ap, x);
+  va_start (ap2, x);
+
+  switch (x)
+    {
+#define test(n, ret, fmt, args) \
+    case n:                                    \
+      vfprintf (stdout, fmt, ap);              \
+      if (vfprintf (stdout, fmt, ap2) != ret)  \
+       abort ();                               \
+      break;
+#include "vfprintf-1.c"
+#undef test
+    default:
+      abort ();
+    }
+
+  va_end (ap);
+  va_end (ap2);
+}
+
+int
+main (void)
+{
+#define test(n, ret, fmt, args) \
+  inner args;
+#include "vfprintf-1.c"
+#undef test
+  return 0;
+}
+
+#else
+  test (0, 5, "hello", (0));
+  test (1, 6, "hello\n", (1));
+  test (2, 1, "a", (2));
+  test (3, 0, "", (3));
+  test (4, 5, "%s", (4, "hello"));
+  test (5, 6, "%s", (5, "hello\n"));
+  test (6, 1, "%s", (6, "a"));
+  test (7, 0, "%s", (7, ""));
+  test (8, 1, "%c", (8, 'x'));
+  test (9, 7, "%s\n", (9, "hello\n"));
+  test (10, 2, "%d\n", (10, 0));
+#endif
diff --git a/gcc/testsuite/gcc.c-torture/execute/vfprintf-chk-1.c b/gcc/testsuite/gcc.c-torture/execute/vfprintf-chk-1.c
new file mode 100644 (file)
index 0000000..f8f964c
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef test
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+volatile int should_optimize;
+
+int
+__attribute__((noinline))
+__vfprintf_chk (FILE *f, int flag, const char *fmt, va_list ap)
+{
+#ifdef __OPTIMIZE__
+  if (should_optimize)
+    abort ();
+#endif
+  should_optimize = 1;
+  return vfprintf (f, fmt, ap);
+}
+
+void
+inner (int x, ...)
+{
+  va_list ap, ap2;
+  va_start (ap, x);
+  va_start (ap2, x);
+
+  switch (x)
+    {
+#define test(n, ret, opt, fmt, args) \
+    case n:                                            \
+      should_optimize = opt;                           \
+      __vfprintf_chk (stdout, 1, fmt, ap);             \
+      if (! should_optimize)                           \
+       abort ();                                       \
+      should_optimize = 0;                             \
+      if (__vfprintf_chk (stdout, 1, fmt, ap2) != ret) \
+       abort ();                                       \
+      if (! should_optimize)                           \
+       abort ();                                       \
+      break;
+#include "vfprintf-chk-1.c"
+#undef test
+    default:
+      abort ();
+    }
+
+  va_end (ap);
+  va_end (ap2);
+}
+
+int
+main (void)
+{
+#define test(n, ret, opt, fmt, args) \
+  inner args;
+#include "vfprintf-chk-1.c"
+#undef test
+  return 0;
+}
+
+#else
+  test (0, 5, 1, "hello", (0));
+  test (1, 6, 1, "hello\n", (1));
+  test (2, 1, 1, "a", (2));
+  test (3, 0, 1, "", (3));
+  test (4, 5, 0, "%s", (4, "hello"));
+  test (5, 6, 0, "%s", (5, "hello\n"));
+  test (6, 1, 0, "%s", (6, "a"));
+  test (7, 0, 0, "%s", (7, ""));
+  test (8, 1, 0, "%c", (8, 'x'));
+  test (9, 7, 0, "%s\n", (9, "hello\n"));
+  test (10, 2, 0, "%d\n", (10, 0));
+#endif
diff --git a/gcc/testsuite/gcc.c-torture/execute/vprintf-1.c b/gcc/testsuite/gcc.c-torture/execute/vprintf-1.c
new file mode 100644 (file)
index 0000000..9f1b8bf
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef test
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+void
+inner (int x, ...)
+{
+  va_list ap, ap2;
+  va_start (ap, x);
+  va_start (ap2, x);
+
+  switch (x)
+    {
+#define test(n, ret, fmt, args) \
+    case n:                            \
+      vprintf (fmt, ap);               \
+      if (vprintf (fmt, ap2) != ret)   \
+       abort ();                       \
+      break;
+#include "vprintf-1.c"
+#undef test
+    default:
+      abort ();
+    }
+
+  va_end (ap);
+  va_end (ap2);
+}
+
+int
+main (void)
+{
+#define test(n, ret, fmt, args) \
+  inner args;
+#include "vprintf-1.c"
+#undef test
+  return 0;
+}
+
+#else
+  test (0, 5, "hello", (0));
+  test (1, 6, "hello\n", (1));
+  test (2, 1, "a", (2));
+  test (3, 0, "", (3));
+  test (4, 5, "%s", (4, "hello"));
+  test (5, 6, "%s", (5, "hello\n"));
+  test (6, 1, "%s", (6, "a"));
+  test (7, 0, "%s", (7, ""));
+  test (8, 1, "%c", (8, 'x'));
+  test (9, 7, "%s\n", (9, "hello\n"));
+  test (10, 2, "%d\n", (10, 0));
+#endif
diff --git a/gcc/testsuite/gcc.c-torture/execute/vprintf-chk-1.c b/gcc/testsuite/gcc.c-torture/execute/vprintf-chk-1.c
new file mode 100644 (file)
index 0000000..ca62f8b
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef test
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+volatile int should_optimize;
+
+int
+__attribute__((noinline))
+__vprintf_chk (int flag, const char *fmt, va_list ap)
+{
+#ifdef __OPTIMIZE__
+  if (should_optimize)
+    abort ();
+#endif
+  should_optimize = 1;
+  return vprintf (fmt, ap);
+}
+
+void
+inner (int x, ...)
+{
+  va_list ap, ap2;
+  va_start (ap, x);
+  va_start (ap2, x);
+
+  switch (x)
+    {
+#define test(n, ret, opt, fmt, args) \
+    case n:                                    \
+      should_optimize = opt;                   \
+      __vprintf_chk (1, fmt, ap);              \
+      if (! should_optimize)                   \
+       abort ();                               \
+      should_optimize = 0;                     \
+      if (__vprintf_chk (1, fmt, ap2) != ret)  \
+       abort ();                               \
+      if (! should_optimize)                   \
+       abort ();                               \
+      break;
+#include "vprintf-chk-1.c"
+#undef test
+    default:
+      abort ();
+    }
+
+  va_end (ap);
+  va_end (ap2);
+}
+
+int
+main (void)
+{
+#define test(n, ret, opt, fmt, args) \
+  inner args;
+#include "vprintf-chk-1.c"
+#undef test
+  return 0;
+}
+
+#else
+  test (0, 5, 0, "hello", (0));
+  test (1, 6, 1, "hello\n", (1));
+  test (2, 1, 1, "a", (2));
+  test (3, 0, 1, "", (3));
+  test (4, 5, 0, "%s", (4, "hello"));
+  test (5, 6, 0, "%s", (5, "hello\n"));
+  test (6, 1, 0, "%s", (6, "a"));
+  test (7, 0, 0, "%s", (7, ""));
+  test (8, 1, 0, "%c", (8, 'x'));
+  test (9, 7, 0, "%s\n", (9, "hello\n"));
+  test (10, 2, 0, "%d\n", (10, 0));
+#endif
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
new file mode 100644 (file)
index 0000000..404b711
--- /dev/null
@@ -0,0 +1,436 @@
+/* { 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))
+test1 (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) != 0)
+    abort ();
+  if (__builtin_object_size ("abcdefg", 0) != sizeof ("abcdefg"))
+    abort ();
+  if (__builtin_object_size ("abcd\0efg", 0) != sizeof ("abcd\0efg"))
+    abort ();
+  if (__builtin_object_size (&"abcd\0efg", 0) != sizeof ("abcd\0efg"))
+    abort ();
+  if (__builtin_object_size (&"abcd\0efg"[0], 0) != sizeof ("abcd\0efg"))
+    abort ();
+  if (__builtin_object_size (&"abcd\0efg"[4], 0) != sizeof ("abcd\0efg") - 4)
+    abort ();
+  if (__builtin_object_size ("abcd\0efg" + 5, 0) != sizeof ("abcd\0efg") - 5)
+    abort ();
+  if (__builtin_object_size (L"abcdefg", 0) != sizeof (L"abcdefg"))
+    abort ();
+  r = (char *) L"abcd\0efg";
+  if (__builtin_object_size (r + 2, 0) != sizeof (L"abcd\0efg") - 2)
+    abort ();
+}
+
+size_t l1 = 1;
+
+void
+__attribute__ ((noinline))
+test2 (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 ();
+  r = &buf3[18];
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+       r = &a.buf1[9];
+      else if (i == l1)
+       r = &a.buf2[9];
+      else if (i == l1 + 1)
+       r = &buf3[5];
+      else if (i == l1 + 2)
+       r = &a.buf1[4];
+    }
+  if (__builtin_object_size (r + 12, 0) != 4)
+    abort ();
+}
+
+void
+__attribute__ ((noinline))
+test3 (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 ();
+}
+
+struct S { unsigned int a; };
+
+char *
+__attribute__ ((noinline))
+test4 (char *x, int y)
+{
+  register int i;
+  struct A *p;
+
+  for (i = 0; i < y; i++)
+    {
+      p = (struct A *) x;
+      x = (char *) &p[1];
+      if (__builtin_object_size (p, 0) != (size_t) -1)
+       abort ();
+    }
+  return x;
+}
+
+void
+__attribute__ ((noinline))
+test5 (size_t x)
+{
+  char buf[64];
+  char *p = &buf[8];
+  size_t i;
+
+  for (i = 0; i < x; ++i)
+    p = p + 4;
+  /* My understanding of ISO C99 6.5.6 is that a conforming
+     program will not end up with p equal to &buf[0]
+     through &buf[7], i.e. calling this function with say
+     UINTPTR_MAX / 4 results in undefined behaviour.
+     If that's true, then the maximum number of remaining
+     bytes from p until end of the object is 56, otherwise
+     it would be 64 (or conservative (size_t) -1 == unknown).  */
+  if (__builtin_object_size (p, 0) != sizeof (buf) - 8)
+    abort ();
+  memset (p, ' ', sizeof (buf) - 8 - 4 * 4);
+}
+
+void
+__attribute__ ((noinline))
+test6 (size_t x)
+{
+  struct T { char buf[64]; char buf2[64]; } t;
+  char *p = &t.buf[8];
+  size_t i;
+
+  for (i = 0; i < x; ++i)
+    p = p + 4;
+  if (__builtin_object_size (p, 0) != sizeof (t) - 8)
+    abort ();
+  memset (p, ' ', sizeof (t) - 8 - 4 * 4);
+}
+
+void
+__attribute__ ((noinline))
+test7 (void)
+{
+  char buf[64];
+  struct T { char buf[64]; char buf2[64]; } t;
+  char *p = &buf[64], *q = &t.buf[64];
+
+  if (__builtin_object_size (p + 64, 0) != 0)
+    abort ();
+  if (__builtin_object_size (q + 63, 0) != sizeof (t) - 64 - 63)
+    abort ();
+  if (__builtin_object_size (q + 64, 0) != sizeof (t) - 64 - 64)
+    abort ();
+  if (__builtin_object_size (q + 256, 0) != 0)
+    abort ();
+}
+
+void
+__attribute__ ((noinline))
+test8 (void)
+{
+  struct T { char buf[10]; char buf2[10]; } t;
+  char *p = &t.buf2[-4];
+  char *q = &t.buf2[0];
+  if (__builtin_object_size (p, 0) != sizeof (t) - 10 + 4)
+    abort ();
+  if (__builtin_object_size (q, 0) != sizeof (t) - 10)
+    abort ();
+  /* GCC only handles additions, not subtractions.  */
+  q = q - 8;
+  if (__builtin_object_size (q, 0) != (size_t) -1
+      && __builtin_object_size (q, 0) != sizeof (t) - 10 + 8)
+    abort ();
+  p = &t.buf[-4];
+  if (__builtin_object_size (p, 0) != 0)
+    abort ();
+}
+
+int
+main (void)
+{
+  struct S s[10];
+  __asm ("" : "=r" (l1) : "0" (l1));
+  test1 (main, 6);
+  test2 ();
+  test3 ();
+  test4 ((char *) s, 10);
+  test5 (4);
+  test6 (4);
+  test7 ();
+  test8 ();
+  exit (0);
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
new file mode 100644 (file)
index 0000000..4071c25
--- /dev/null
@@ -0,0 +1,393 @@
+/* { 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))
+test1 (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) != 0)
+    abort ();
+  if (__builtin_object_size ("abcdefg", 1) != sizeof ("abcdefg"))
+    abort ();
+  if (__builtin_object_size ("abcd\0efg", 1) != sizeof ("abcd\0efg"))
+    abort ();
+  if (__builtin_object_size (&"abcd\0efg", 1) != sizeof ("abcd\0efg"))
+    abort ();
+  if (__builtin_object_size (&"abcd\0efg"[0], 1) != sizeof ("abcd\0efg"))
+    abort ();
+  if (__builtin_object_size (&"abcd\0efg"[4], 1) != sizeof ("abcd\0efg") - 4)
+    abort ();
+  if (__builtin_object_size ("abcd\0efg" + 5, 1) != sizeof ("abcd\0efg") - 5)
+    abort ();
+  if (__builtin_object_size (L"abcdefg", 1) != sizeof (L"abcdefg"))
+    abort ();
+  r = (char *) L"abcd\0efg";
+  if (__builtin_object_size (r + 2, 1) != sizeof (L"abcd\0efg") - 2)
+    abort ();
+}
+
+size_t l1 = 1;
+
+void
+__attribute__ ((noinline))
+test2 (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))
+test3 (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 ();
+}
+
+struct S { unsigned int a; };
+
+char *
+__attribute__ ((noinline))
+test4 (char *x, int y)
+{
+  register int i;
+  struct A *p;
+
+  for (i = 0; i < y; i++)
+    {
+      p = (struct A *) x;
+      x = (char *) &p[1];
+      if (__builtin_object_size (p, 1) != (size_t) -1)
+       abort ();
+    }
+  return x;
+}
+
+void
+__attribute__ ((noinline))
+test5 (size_t x)
+{
+  struct T { char buf[64]; char buf2[64]; } t;
+  char *p = &t.buf[8];
+  size_t i;
+
+  for (i = 0; i < x; ++i)
+    p = p + 4;
+  if (__builtin_object_size (p, 1) != sizeof (t.buf) - 8)
+    abort ();
+  memset (p, ' ', sizeof (t.buf) - 8 - 4 * 4);
+}
+
+void
+__attribute__ ((noinline))
+test6 (void)
+{
+  char buf[64];
+  struct T { char buf[64]; char buf2[64]; } t;
+  char *p = &buf[64], *q = &t.buf[64];
+
+  if (__builtin_object_size (p + 64, 1) != 0)
+    abort ();
+  if (__builtin_object_size (q + 0, 1) != 0)
+    abort ();
+  if (__builtin_object_size (q + 64, 1) != 0)
+    abort ();
+}
+
+void
+__attribute__ ((noinline))
+test7 (void)
+{
+  struct T { char buf[10]; char buf2[10]; } t;
+  char *p = &t.buf2[-4];
+  char *q = &t.buf2[0];
+  if (__builtin_object_size (p, 1) != 0)
+    abort ();
+  if (__builtin_object_size (q, 1) != sizeof (t.buf2))
+    abort ();
+  q = &t.buf[10];
+  if (__builtin_object_size (q, 1) != 0)
+    abort ();
+  q = &t.buf[11];
+  if (__builtin_object_size (q, 1) != 0)
+    abort ();
+  p = &t.buf[-4];
+  if (__builtin_object_size (p, 1) != 0)
+    abort ();
+}
+
+int
+main (void)
+{
+  struct S s[10];
+  __asm ("" : "=r" (l1) : "0" (l1));
+  test1 (main, 6);
+  test2 ();
+  test3 ();
+  test4 ((char *) s, 10);
+  test5 (4);
+  test6 ();
+  test7 ();
+  exit (0);
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
new file mode 100644 (file)
index 0000000..572ecda
--- /dev/null
@@ -0,0 +1,446 @@
+/* { 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))
+test1 (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, 2)
+      != sizeof (a) - __builtin_offsetof (struct A, a) - 3)
+    abort ();
+  if (__builtin_object_size (&a.c[9], 2)
+      != sizeof (a) - __builtin_offsetof (struct A, c) - 9)
+    abort ();
+  if (__builtin_object_size (q, 2) != 0)
+    abort ();
+  if (__builtin_object_size (r, 2)
+      != sizeof (a) - __builtin_offsetof (struct A, c) - 1)
+    abort ();
+  if (x < 6)
+    r = &w[2].a[1];
+  else
+    r = &a.a[6];
+  if (__builtin_object_size (&y, 2)
+      != sizeof (y))
+    abort ();
+  if (__builtin_object_size (w, 2)
+      != sizeof (w))
+    abort ();
+  if (__builtin_object_size (&y.b, 2)
+      != sizeof (a) - __builtin_offsetof (struct A, b))
+    abort ();
+  if (__builtin_object_size (r, 2)
+      != sizeof (a) - __builtin_offsetof (struct A, a) - 6)
+    abort ();
+  if (x < 20)
+    r = malloc (30);
+  else
+    r = calloc (2, 16);
+  if (__builtin_object_size (r, 2) != 30)
+    abort ();
+  if (x < 20)
+    r = malloc (30);
+  else
+    r = calloc (2, 14);
+  if (__builtin_object_size (r, 2) != 2 * 14)
+    abort ();
+  if (x < 30)
+    r = malloc (sizeof (a));
+  else
+    r = &a.a[3];
+  if (__builtin_object_size (r, 2)
+      != sizeof (a) - __builtin_offsetof (struct A, a) - 3)
+    abort ();
+  r = memcpy (r, "a", 2);
+  if (__builtin_object_size (r, 2)
+      != sizeof (a) - __builtin_offsetof (struct A, a) - 3)
+    abort ();
+  r = memcpy (r + 2, "b", 2) + 2;
+  if (__builtin_object_size (r, 2)
+      != sizeof (a) - __builtin_offsetof (struct A, a) - 3 - 4)
+    abort ();
+  r = &a.a[4];
+  r = memset (r, 'a', 2);
+  if (__builtin_object_size (r, 2)
+      != sizeof (a) - __builtin_offsetof (struct A, a) - 4)
+    abort ();
+  r = memset (r + 2, 'b', 2) + 2;
+  if (__builtin_object_size (r, 2)
+      != sizeof (a) - __builtin_offsetof (struct A, a) - 8)
+    abort ();
+  r = &a.a[1];
+  r = strcpy (r, "ab");
+  if (__builtin_object_size (r, 2)
+      != sizeof (a) - __builtin_offsetof (struct A, a) - 1)
+    abort ();
+  r = strcpy (r + 2, "cd") + 2;
+  if (__builtin_object_size (r, 2)
+      != sizeof (a) - __builtin_offsetof (struct A, a) - 5)
+    abort ();
+  if (__builtin_object_size (exta, 2) != 0)
+    abort ();
+  if (__builtin_object_size (exta + 10, 2) != 0)
+    abort ();
+  if (__builtin_object_size (&exta[5], 2) != 0)
+    abort ();
+  if (__builtin_object_size (extb, 2) != sizeof (extb))
+    abort ();
+  if (__builtin_object_size (extb + 10, 2) != sizeof (extb) - 10)
+    abort ();
+  if (__builtin_object_size (&extb[5], 2) != sizeof (extb) - 5)
+    abort ();
+  if (__builtin_object_size (var, 2) != 0)
+    abort ();
+  if (__builtin_object_size (var + 10, 2) != 0)
+    abort ();
+  if (__builtin_object_size (&var[5], 2) != 0)
+    abort ();
+  if (__builtin_object_size (zerol, 2) != 0)
+    abort ();
+  if (__builtin_object_size (&zerol, 2) != 0)
+    abort ();
+  if (__builtin_object_size (&zerol[0], 2) != 0)
+    abort ();
+  if (__builtin_object_size (zerol[0].a, 2) != 0)
+    abort ();
+  if (__builtin_object_size (&zerol[0].a[0], 2) != 0)
+    abort ();
+  if (__builtin_object_size (&zerol[0].b, 2) != 0)
+    abort ();
+  if (__builtin_object_size ("abcdefg", 2) != sizeof ("abcdefg"))
+    abort ();
+  if (__builtin_object_size ("abcd\0efg", 2) != sizeof ("abcd\0efg"))
+    abort ();
+  if (__builtin_object_size (&"abcd\0efg", 2) != sizeof ("abcd\0efg"))
+    abort ();
+  if (__builtin_object_size (&"abcd\0efg"[0], 2) != sizeof ("abcd\0efg"))
+    abort ();
+  if (__builtin_object_size (&"abcd\0efg"[4], 2) != sizeof ("abcd\0efg") - 4)
+    abort ();
+  if (__builtin_object_size ("abcd\0efg" + 5, 2) != sizeof ("abcd\0efg") - 5)
+    abort ();
+  if (__builtin_object_size (L"abcdefg", 2) != sizeof (L"abcdefg"))
+    abort ();
+  r = (char *) L"abcd\0efg";
+  if (__builtin_object_size (r + 2, 2) != sizeof (L"abcd\0efg") - 2)
+    abort ();
+}
+
+size_t l1 = 1;
+
+void
+__attribute__ ((noinline))
+test2 (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, 2) != 3)
+    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, 2) != 0)
+    abort ();
+  r = &buf3[2];
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+       r = &a.buf1[1];
+      else if (i == l1)
+       r = &a.buf1[2];
+      else if (i == l1 + 1)
+       r = &buf3[5];
+      else if (i == l1 + 2)
+       r = &a.buf1[4];
+    }
+  if (__builtin_object_size (r, 2) != 15)
+    abort ();
+  r += 8;
+  if (__builtin_object_size (r, 2) != 7)
+    abort ();
+  if (__builtin_object_size (r + 6, 2) != 1)
+    abort ();
+  r = &buf3[18];
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+       r = &a.buf1[9];
+      else if (i == l1)
+       r = &a.buf2[9];
+      else if (i == l1 + 1)
+       r = &buf3[5];
+      else if (i == l1 + 2)
+       r = &a.buf1[4];
+    }
+  if (__builtin_object_size (r + 12, 2) != 0)
+    abort ();
+}
+
+void
+__attribute__ ((noinline))
+test3 (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, 2) != sizeof (buf4))
+    abort ();
+  if (__builtin_object_size (&buf4, 2) != sizeof (buf4))
+    abort ();
+  if (__builtin_object_size (&buf4[0], 2) != sizeof (buf4))
+    abort ();
+  if (__builtin_object_size (&buf4[1], 2) != sizeof (buf4) - 1)
+    abort ();
+  if (__builtin_object_size (&x, 2) != sizeof (x))
+    abort ();
+  if (__builtin_object_size (&x.a, 2) != sizeof (x))
+    abort ();
+  if (__builtin_object_size (&x.a[0], 2) != sizeof (x))
+    abort ();
+  if (__builtin_object_size (&x.a[0].a, 2) != sizeof (x))
+    abort ();
+  if (__builtin_object_size (&x.a[0].a[0], 2) != sizeof (x))
+    abort ();
+  if (__builtin_object_size (&x.a[0].a[3], 2) != sizeof (x) - 3)
+    abort ();
+  if (__builtin_object_size (&x.a[0].b, 2)
+      != sizeof (x) - __builtin_offsetof (struct A, b))
+    abort ();
+  if (__builtin_object_size (&x.a[1].c, 2)
+      != sizeof (x) - sizeof (struct A) - __builtin_offsetof (struct A, c))
+    abort ();
+  if (__builtin_object_size (&x.a[1].c[0], 2)
+      != sizeof (x) - sizeof (struct A) - __builtin_offsetof (struct A, c))
+    abort ();
+  if (__builtin_object_size (&x.a[1].c[3], 2)
+      != sizeof (x) - sizeof (struct A) - __builtin_offsetof (struct A, c) - 3)
+    abort ();
+  if (__builtin_object_size (&x.b, 2)
+      != sizeof (x) - __builtin_offsetof (struct B, b))
+    abort ();
+  if (__builtin_object_size (&x.b.a, 2)
+      != sizeof (x) - __builtin_offsetof (struct B, b))
+    abort ();
+  if (__builtin_object_size (&x.b.a[0], 2)
+      != sizeof (x) - __builtin_offsetof (struct B, b))
+    abort ();
+  if (__builtin_object_size (&x.b.a[3], 2)
+      != sizeof (x) - __builtin_offsetof (struct B, b) - 3)
+    abort ();
+  if (__builtin_object_size (&x.b.b, 2)
+      != sizeof (x) - __builtin_offsetof (struct B, b)
+        - __builtin_offsetof (struct A, b))
+    abort ();
+  if (__builtin_object_size (&x.b.c, 2)
+      != sizeof (x) - __builtin_offsetof (struct B, b)
+        - __builtin_offsetof (struct A, c))
+    abort ();
+  if (__builtin_object_size (&x.b.c[0], 2)
+      != sizeof (x) - __builtin_offsetof (struct B, b)
+        - __builtin_offsetof (struct A, c))
+    abort ();
+  if (__builtin_object_size (&x.b.c[3], 2)
+      != sizeof (x) - __builtin_offsetof (struct B, b)
+        - __builtin_offsetof (struct A, c) - 3)
+    abort ();
+  if (__builtin_object_size (&x.c, 2)
+      != sizeof (x) - __builtin_offsetof (struct B, c))
+    abort ();
+  if (__builtin_object_size (&x.c[0], 2)
+      != sizeof (x) - __builtin_offsetof (struct B, c))
+    abort ();
+  if (__builtin_object_size (&x.c[1], 2)
+      != sizeof (x) - __builtin_offsetof (struct B, c) - 1)
+    abort ();
+  if (__builtin_object_size (&x.d, 2)
+      != sizeof (x) - __builtin_offsetof (struct B, d))
+    abort ();
+  if (__builtin_object_size (&x.e, 2)
+      != sizeof (x) - __builtin_offsetof (struct B, e))
+    abort ();
+  if (__builtin_object_size (&x.f, 2)
+      != sizeof (x) - __builtin_offsetof (struct B, f))
+    abort ();
+  dp = &__real__ x.f;
+  if (__builtin_object_size (dp, 2)
+      != sizeof (x) - __builtin_offsetof (struct B, f))
+    abort ();
+  dp = &__imag__ x.f;
+  if (__builtin_object_size (dp, 2)
+      != sizeof (x) - __builtin_offsetof (struct B, f)
+        - sizeof (x.f) / 2)
+    abort ();
+  dp = &y;
+  if (__builtin_object_size (dp, 2) != sizeof (y))
+    abort ();
+  if (__builtin_object_size (&z, 2) != sizeof (z))
+    abort ();
+  dp = &__real__ z;
+  if (__builtin_object_size (dp, 2) != sizeof (z))
+    abort ();
+  dp = &__imag__ z;
+  if (__builtin_object_size (dp, 2) != sizeof (z) / 2)
+    abort ();
+}
+
+struct S { unsigned int a; };
+
+char *
+__attribute__ ((noinline))
+test4 (char *x, int y)
+{
+  register int i;
+  struct A *p;
+
+  for (i = 0; i < y; i++)
+    {
+      p = (struct A *) x;
+      x = (char *) &p[1];
+      if (__builtin_object_size (p, 2) != 0)
+       abort ();
+    }
+  return x;
+}
+
+void
+__attribute__ ((noinline))
+test5 (size_t x)
+{
+  char buf[64];
+  char *p = &buf[8];
+  size_t i;
+
+  for (i = 0; i < x; ++i)
+    p = p + 4;
+  if (__builtin_object_size (p, 2) != 0)
+    abort ();
+  memset (p, ' ', sizeof (buf) - 8 - 4 * 4);
+}
+
+void
+__attribute__ ((noinline))
+test6 (size_t x)
+{
+  struct T { char buf[64]; char buf2[64]; } t;
+  char *p = &t.buf[8];
+  size_t i;
+
+  for (i = 0; i < x; ++i)
+    p = p + 4;
+  if (__builtin_object_size (p, 2) != 0)
+    abort ();
+  memset (p, ' ', sizeof (t) - 8 - 4 * 4);
+}
+
+void
+__attribute__ ((noinline))
+test7 (void)
+{
+  char buf[64];
+  struct T { char buf[64]; char buf2[64]; } t;
+  char *p = &buf[64], *q = &t.buf[64];
+
+  if (__builtin_object_size (p + 64, 2) != 0)
+    abort ();
+  if (__builtin_object_size (q + 63, 2) != sizeof (t) - 64 - 63)
+    abort ();
+  if (__builtin_object_size (q + 64, 2) != sizeof (t) - 64 - 64)
+    abort ();
+  if (__builtin_object_size (q + 256, 2) != 0)
+    abort ();
+}
+
+void
+__attribute__ ((noinline))
+test8 (void)
+{
+  struct T { char buf[10]; char buf2[10]; } t;
+  char *p = &t.buf2[-4];
+  char *q = &t.buf2[0];
+  if (__builtin_object_size (p, 2) != sizeof (t) - 10 + 4)
+    abort ();
+  if (__builtin_object_size (q, 2) != sizeof (t) - 10)
+    abort ();
+  /* GCC only handles additions, not subtractions.  */
+  q = q - 8;
+  if (__builtin_object_size (q, 2) != 0
+      && __builtin_object_size (q, 2) != sizeof (t) - 10 + 8)
+    abort ();
+  p = &t.buf[-4];
+  if (__builtin_object_size (p, 2) != 0)
+    abort ();
+}
+
+int
+main (void)
+{
+  struct S s[10];
+  __asm ("" : "=r" (l1) : "0" (l1));
+  test1 (main, 6);
+  test2 ();
+  test3 ();
+  test4 ((char *) s, 10);
+  test5 (4);
+  test6 (4);
+  test7 ();
+  test8 ();
+  exit (0);
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
new file mode 100644 (file)
index 0000000..453c2d0
--- /dev/null
@@ -0,0 +1,407 @@
+/* { 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))
+test1 (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, 3) != sizeof (a.a) - 3)
+    abort ();
+  if (__builtin_object_size (&a.c[9], 3)
+      != sizeof (a.c) - 9)
+    abort ();
+  if (__builtin_object_size (q, 3) != 0)
+    abort ();
+  if (__builtin_object_size (r, 3) != sizeof (a.a) - 9)
+    abort ();
+  if (x < 6)
+    r = &w[2].a[1];
+  else
+    r = &a.a[6];
+  if (__builtin_object_size (&y, 3) != sizeof (y))
+    abort ();
+  if (__builtin_object_size (w, 3) != sizeof (w))
+    abort ();
+  if (__builtin_object_size (&y.b, 3) != sizeof (a.b))
+    abort ();
+  if (__builtin_object_size (r, 3) != sizeof (a.a) - 6)
+    abort ();
+  if (x < 20)
+    r = malloc (30);
+  else
+    r = calloc (2, 16);
+  if (__builtin_object_size (r, 3) != 30)
+    abort ();
+  if (x < 20)
+    r = malloc (30);
+  else
+    r = calloc (2, 14);
+  if (__builtin_object_size (r, 3) != 2 * 14)
+    abort ();
+  if (x < 30)
+    r = malloc (sizeof (a));
+  else
+    r = &a.a[3];
+  if (__builtin_object_size (r, 3) != sizeof (a.a) - 3)
+    abort ();
+  r = memcpy (r, "a", 2);
+  if (__builtin_object_size (r, 3) != sizeof (a.a) - 3)
+    abort ();
+  r = memcpy (r + 2, "b", 2) + 2;
+  if (__builtin_object_size (r, 3) != sizeof (a.a) - 3 - 4)
+    abort ();
+  r = &a.a[4];
+  r = memset (r, 'a', 2);
+  if (__builtin_object_size (r, 3) != sizeof (a.a) - 4)
+    abort ();
+  r = memset (r + 2, 'b', 2) + 2;
+  if (__builtin_object_size (r, 3) != sizeof (a.a) - 8)
+    abort ();
+  r = &a.a[1];
+  r = strcpy (r, "ab");
+  if (__builtin_object_size (r, 3) != sizeof (a.a) - 1)
+    abort ();
+  r = strcpy (r + 2, "cd") + 2;
+  if (__builtin_object_size (r, 3) != sizeof (a.a) - 5)
+    abort ();
+  if (__builtin_object_size (exta, 3) != 0)
+    abort ();
+  if (__builtin_object_size (exta + 10, 3) != 0)
+    abort ();
+  if (__builtin_object_size (&exta[5], 3) != 0)
+    abort ();
+  if (__builtin_object_size (extb, 3) != sizeof (extb))
+    abort ();
+  if (__builtin_object_size (extb + 10, 3) != sizeof (extb) - 10)
+    abort ();
+  if (__builtin_object_size (&extb[5], 3) != sizeof (extb) - 5)
+    abort ();
+  if (__builtin_object_size (extc, 3) != 0)
+    abort ();
+  if (__builtin_object_size (extc + 10, 3) != 0)
+    abort ();
+  if (__builtin_object_size (&extc[5], 3) != 0)
+    abort ();
+  if (__builtin_object_size (&extc->a, 3) != 0)
+    abort ();
+  if (__builtin_object_size (&(extc + 10)->b, 3) != 0)
+    abort ();
+  if (__builtin_object_size (&extc[5].c[3], 3) != 0)
+    abort ();
+  if (__builtin_object_size (var, 3) != 0)
+    abort ();
+  if (__builtin_object_size (var + 10, 3) != 0)
+    abort ();
+  if (__builtin_object_size (&var[5], 3) != 0)
+    abort ();
+  if (__builtin_object_size (vara, 3) != 0)
+    abort ();
+  if (__builtin_object_size (vara + 10, 3) != 0)
+    abort ();    
+  if (__builtin_object_size (&vara[5], 3) != 0)
+    abort ();
+  if (__builtin_object_size (&vara[0].a, 3) != 0)
+    abort ();
+  if (__builtin_object_size (&vara[10].a[0], 3) != 0)
+    abort ();
+  if (__builtin_object_size (&vara[5].a[4], 3) != 0)
+    abort ();
+  if (__builtin_object_size (&vara[5].b, 3) != 0)
+    abort ();
+  if (__builtin_object_size (&vara[7].c[7], 3) != 0)
+    abort ();
+  if (__builtin_object_size (zerol, 3) != 0)
+    abort ();
+  if (__builtin_object_size (&zerol, 3) != 0)
+    abort ();
+  if (__builtin_object_size (&zerol[0], 3) != 0)
+    abort ();
+  if (__builtin_object_size (zerol[0].a, 3) != 0)
+    abort ();
+  if (__builtin_object_size (&zerol[0].a[0], 3) != 0)
+    abort ();
+  if (__builtin_object_size (&zerol[0].b, 3) != 0)
+    abort ();
+  if (__builtin_object_size ("abcdefg", 3) != sizeof ("abcdefg"))
+    abort ();
+  if (__builtin_object_size ("abcd\0efg", 3) != sizeof ("abcd\0efg"))
+    abort ();
+  if (__builtin_object_size (&"abcd\0efg", 3) != sizeof ("abcd\0efg"))
+    abort ();
+  if (__builtin_object_size (&"abcd\0efg"[0], 3) != sizeof ("abcd\0efg"))
+    abort ();
+  if (__builtin_object_size (&"abcd\0efg"[4], 3) != sizeof ("abcd\0efg") - 4)
+    abort ();
+  if (__builtin_object_size ("abcd\0efg" + 5, 3) != sizeof ("abcd\0efg") - 5)
+    abort ();
+  if (__builtin_object_size (L"abcdefg", 3) != sizeof (L"abcdefg"))
+    abort ();
+  r = (char *) L"abcd\0efg";
+  if (__builtin_object_size (r + 2, 3) != sizeof (L"abcd\0efg") - 2)
+    abort ();
+}
+
+size_t l1 = 1;
+
+void
+__attribute__ ((noinline))
+test2 (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, 3) != sizeof (a.buf1) - 9)
+    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, 3) != 0)
+    abort ();
+  r = &buf3[1];
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+       r = &a.buf1[6];
+      else if (i == l1)
+       r = &a.buf2[4];
+      else if (i == l1 + 1)
+       r = &buf3[5];
+      else if (i == l1 + 2)
+       r = &a.buf1[2];
+    }
+  if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 6)
+    abort ();
+  r += 2;
+  if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 6 - 2)
+    abort ();
+  if (__builtin_object_size (r + 1, 3) != sizeof (a.buf1) - 6 - 3)
+    abort ();
+}
+
+void
+__attribute__ ((noinline))
+test3 (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, 3) != sizeof (buf4))
+    abort ();
+  if (__builtin_object_size (&buf4, 3) != sizeof (buf4))
+    abort ();
+  if (__builtin_object_size (&buf4[0], 3) != sizeof (buf4))
+    abort ();
+  if (__builtin_object_size (&buf4[1], 3) != sizeof (buf4) - 1)
+    abort ();
+  if (__builtin_object_size (&x, 3) != sizeof (x))
+    abort ();
+  if (__builtin_object_size (&x.a, 3) != sizeof (x.a))
+    abort ();
+  if (__builtin_object_size (&x.a[0], 3) != sizeof (x.a))
+    abort ();
+  if (__builtin_object_size (&x.a[0].a, 3) != sizeof (x.a[0].a))
+    abort ();
+  if (__builtin_object_size (&x.a[0].a[0], 3) != sizeof (x.a[0].a))
+    abort ();
+  if (__builtin_object_size (&x.a[0].a[3], 3) != sizeof (x.a[0].a) - 3)
+    abort ();
+  if (__builtin_object_size (&x.a[0].b, 3) != sizeof (x.a[0].b))
+    abort ();
+  if (__builtin_object_size (&x.a[1].c, 3) != sizeof (x.a[1].c))
+    abort ();
+  if (__builtin_object_size (&x.a[1].c[0], 3) != sizeof (x.a[1].c))
+    abort ();
+  if (__builtin_object_size (&x.a[1].c[3], 3) != sizeof (x.a[1].c) - 3)
+    abort ();
+  if (__builtin_object_size (&x.b, 3) != sizeof (x.b))
+    abort ();
+  if (__builtin_object_size (&x.b.a, 3) != sizeof (x.b.a))
+    abort ();
+  if (__builtin_object_size (&x.b.a[0], 3) != sizeof (x.b.a))
+    abort ();
+  if (__builtin_object_size (&x.b.a[3], 3) != sizeof (x.b.a) - 3)
+    abort ();
+  if (__builtin_object_size (&x.b.b, 3) != sizeof (x.b.b))
+    abort ();
+  if (__builtin_object_size (&x.b.c, 3) != sizeof (x.b.c))
+    abort ();
+  if (__builtin_object_size (&x.b.c[0], 3) != sizeof (x.b.c))
+    abort ();
+  if (__builtin_object_size (&x.b.c[3], 3) != sizeof (x.b.c) - 3)
+    abort ();
+  if (__builtin_object_size (&x.c, 3) != sizeof (x.c))
+    abort ();
+  if (__builtin_object_size (&x.c[0], 3) != sizeof (x.c))
+    abort ();
+  if (__builtin_object_size (&x.c[1], 3) != sizeof (x.c) - 1)
+    abort ();
+  if (__builtin_object_size (&x.d, 3) != sizeof (x.d))
+    abort ();
+  if (__builtin_object_size (&x.e, 3) != sizeof (x.e))
+    abort ();
+  if (__builtin_object_size (&x.f, 3) != sizeof (x.f))
+    abort ();
+  dp = &__real__ x.f;
+  if (__builtin_object_size (dp, 3) != sizeof (x.f) / 2)
+    abort ();
+  dp = &__imag__ x.f;
+  if (__builtin_object_size (dp, 3) != sizeof (x.f) / 2)
+    abort ();
+  dp = &y;
+  if (__builtin_object_size (dp, 3) != sizeof (y))
+    abort ();
+  if (__builtin_object_size (&z, 3) != sizeof (z))
+      abort ();
+  dp = &__real__ z;
+  if (__builtin_object_size (dp, 3) != sizeof (z) / 2)
+    abort ();
+  dp = &__imag__ z;
+  if (__builtin_object_size (dp, 3) != sizeof (z) / 2)
+    abort ();
+}
+
+struct S { unsigned int a; };
+
+char *
+__attribute__ ((noinline))
+test4 (char *x, int y)
+{
+  register int i;
+  struct A *p;
+
+  for (i = 0; i < y; i++)
+    {
+      p = (struct A *) x;
+      x = (char *) &p[1];
+      if (__builtin_object_size (p, 3) != 0)
+       abort ();
+    }
+  return x;
+}
+
+void
+__attribute__ ((noinline))
+test5 (size_t x)
+{
+  struct T { char buf[64]; char buf2[64]; } t;
+  char *p = &t.buf[8];
+  size_t i;
+
+  for (i = 0; i < x; ++i)
+    p = p + 4;
+  if (__builtin_object_size (p, 3) != 0)
+    abort ();
+  memset (p, ' ', sizeof (t.buf) - 8 - 4 * 4);
+}
+
+void
+__attribute__ ((noinline))
+test6 (void)
+{
+  char buf[64];
+  struct T { char buf[64]; char buf2[64]; } t;
+  char *p = &buf[64], *q = &t.buf[64];
+
+  if (__builtin_object_size (p + 64, 3) != 0)
+    abort ();
+  if (__builtin_object_size (q + 0, 3) != 0)
+    abort ();
+  if (__builtin_object_size (q + 64, 3) != 0)
+    abort ();
+}
+
+void
+__attribute__ ((noinline))
+test7 (void)
+{
+  struct T { char buf[10]; char buf2[10]; } t;
+  char *p = &t.buf2[-4];
+  char *q = &t.buf2[0];
+  if (__builtin_object_size (p, 3) != 0)
+    abort ();
+  if (__builtin_object_size (q, 3) != sizeof (t.buf2))
+    abort ();
+  q = &t.buf[10];
+  if (__builtin_object_size (q, 3) != 0)
+    abort ();
+  q = &t.buf[11];
+  if (__builtin_object_size (q, 3) != 0)
+    abort ();
+  p = &t.buf[-4];
+  if (__builtin_object_size (p, 3) != 0)
+    abort ();
+}
+
+int
+main (void)
+{
+  struct S s[10];
+  __asm ("" : "=r" (l1) : "0" (l1));
+  test1 (main, 6);
+  test2 ();
+  test3 ();
+  test4 ((char *) s, 10);
+  test5 (4);
+  test6 ();
+  test7 ();
+  exit (0);
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
new file mode 100644 (file)
index 0000000..4cb580e
--- /dev/null
@@ -0,0 +1,56 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-O2" } */
+
+typedef __SIZE_TYPE__ size_t;
+extern void abort (void);
+extern char buf[0x40000000];
+
+void
+test1 (size_t x)
+{
+  char *p = &buf[8];
+  size_t i;
+
+  for (i = 0; i < x; ++i)
+    p = p + 4;
+  if (__builtin_object_size (p, 0) != sizeof (buf) - 8)
+    abort ();
+}
+
+void
+test2 (size_t x)
+{
+  char *p = &buf[8];
+  size_t i;
+
+  for (i = 0; i < x; ++i)
+    p = p + 4;
+  if (__builtin_object_size (p, 1) != sizeof (buf) - 8)
+    abort ();
+}
+
+void
+test3 (size_t x)
+{
+  char *p = &buf[8];
+  size_t i;
+
+  for (i = 0; i < x; ++i)
+    p = p + 4;
+  if (__builtin_object_size (p, 2) != 0)
+    abort ();
+}
+
+void
+test4 (size_t x)
+{
+  char *p = &buf[8];
+  size_t i;
+
+  for (i = 0; i < x; ++i)
+    p = p + 4;
+  if (__builtin_object_size (p, 3) != 0)
+    abort ();
+}
+
+/* { dg-final { scan-assembler-not "abort" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c
new file mode 100644 (file)
index 0000000..aaf3288
--- /dev/null
@@ -0,0 +1,113 @@
+/* Test whether buffer overflow warnings for __*_chk builtins
+   are emitted properly.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -std=gnu99" } */
+
+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;
+  char *p = &buf1[10], *q;
+
+  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);
+
+  p = p + 10;
+  memset (p, 'd', 0);
+  q = strcpy (p, ""); /* { dg-warning "will always overflow" "strcpy" } */
+
+  /* This invokes undefined behaviour, since we are past the end of buf1.  */
+  p = p + 10;
+  memset (p, 'd', 1); /* { dg-warning "will always overflow" "memset" } */
+
+  memset (q, 'd', 0);
+  memset (q, 'd', 1); /* { dg-warning "will always overflow" "memset" } */
+  q = q - 10;
+  memset (q, 'd', 10);
+}
+
+char *str = "ABCDEFG";
+typedef struct { char b[16]; } H;
+
+/* Some brown paper bag bugs found in real applications.
+   This test is here merely for amusement.  */
+
+void
+test2 (const H h)
+{
+  char c;
+  strncpy (&c, str, 3); /* { dg-warning "will always overflow" "strncpy" } */
+
+  struct { char b[4]; } x;
+  sprintf (x.b, "%s", "ABCD"); /* { dg-warning "will always overflow" "sprintf" } */
+
+  unsigned int i;
+  memcpy (&i, &h, sizeof (h)); /* { dg-warning "will always overflow" "memcpy" } */
+
+  unsigned char buf[21];
+  memset (buf + 16, 0, 8); /* { dg-warning "will always overflow" "memset" } */
+
+  typedef struct { int i, j, k, l; } S;
+  S *s[3];
+  memset (s, 0, sizeof (S) * 3); /* { dg-warning "will always overflow" "memset" } */
+
+  struct T { char a[8]; char b[4]; char c[10]; } t;
+  stpcpy (t.c,"Testing..."); /* { dg-warning "will always overflow" "stpcpy" } */
+
+  char b1[7];
+  char b2[4];
+  memset (b1, 0, sizeof (b1));
+  memset (b2, 0, sizeof (b1)); /* { dg-warning "will always overflow" "memset" } */
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-2.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-2.c
new file mode 100644 (file)
index 0000000..adccd0f
--- /dev/null
@@ -0,0 +1,137 @@
+/* This file was miscompiled by an earlier version of the object size
+   checking patch.  Object size in one of the memcpy calls was
+   incorrectly determined to be 0 while it should be (size_t) -1
+   (== unknown).  */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+#include "../gcc.c-torture/execute/builtins/chk.h"
+   
+void *bar (int);
+extern void *malloc (__SIZE_TYPE__);
+
+struct A
+{
+  int i, j, k;
+};
+
+/* Here all object sizes are not known at compile time.  There
+   should be no warning, nor any checker functions called.  */
+
+void
+foo (const struct A *x, int y, const unsigned char *z)
+{
+  unsigned int b;
+  unsigned char *c = 0;
+
+  b = (x->i & 0xff) == 1 ? 3 : 4;
+  if (y)
+    c = bar (x->j * x->k);
+
+  const unsigned char *d = z;
+  unsigned char *e = c;
+  unsigned char *f = c + x->j * x->k;
+  int g = 0;
+
+  while (e < f)
+    {
+      unsigned int h = *d++;
+
+      if (h & 128)
+       {
+         h = h - 128;
+         g = e + h * b > f;
+         if (g)
+           h = (f - e) / b;
+         if (b < 4)
+           do
+             {
+               memcpy (e, d, 3);
+               e += 3;
+             }
+           while (--h);
+         else
+           do
+             {
+               memcpy (e, d, 4);
+               e += 4;
+             }
+           while (--h);
+         d += b;
+       }
+      else
+       {
+         h *= b;
+         g = e + h > f;
+         if (g)
+           h = f - e;
+         memcpy (e, d, h);
+         e += h;
+         d += h;
+       }
+    }
+}
+
+/* The same routine, slightly modified:
+   1) c has known size at compile time
+   2) e += h was changed into e += 16.
+      GCC could actually through VRP determine that
+      in e += h is (h >= 0 && h <= 127), thus know
+      it is pointer addition and not subtraction and
+      know e's __builtin_object_size (e, 0) is at 512,
+      but we are not there yet.  */
+
+unsigned char *
+baz (const struct A *x, const unsigned char *z)
+{
+  unsigned int b;
+  unsigned char *c = 0;
+
+  b = (x->i & 0xff) == 1 ? 3 : 4;
+  c = malloc (512);
+
+  const unsigned char *d = z;
+  unsigned char *e = c;
+  unsigned char *f = c + x->j * x->k;
+  int g = 0;
+
+  while (e < f)
+    {
+      unsigned int h = *d++;
+
+      if (h & 128)
+       {
+         h = h - 128;
+         g = e + h * b > f;
+         if (g)
+           h = (f - e) / b;
+         if (b < 4)
+           do
+             {
+               memcpy (e, d, 3);
+               e += 3;
+             }
+           while (--h);
+         else
+           do
+             {
+               memcpy (e, d, 513); /* { dg-warning "will always overflow" "memcpy" } */
+               e += 4;
+             }
+           while (--h);
+         d += b;
+       }
+      else
+       {
+         h *= b;
+         g = e + h > f;
+         if (g)
+           h = f - e;
+         memcpy (e, d, h);
+         /* e += h; */
+         e += 16;
+         d += h;
+       }
+    }
+  return c;
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-1.c
new file mode 100644 (file)
index 0000000..b00c0a6
--- /dev/null
@@ -0,0 +1,40 @@
+/* { 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;
+  fprintf (fp, "hello");
+  vi1 = 0;
+  fprintf (fp, "hello\n");
+  vi2 = 0;
+  fprintf (fp, "a");
+  vi3 = 0;
+  fprintf (fp, "");
+  vi4 = 0;
+  fprintf (fp, "%s", "hello");
+  vi5 = 0;
+  fprintf (fp, "%s", "hello\n");
+  vi6 = 0;
+  fprintf (fp, "%s", "a");
+  vi7 = 0;
+  fprintf (fp, "%c", 'x');
+  vi8 = 0;
+  fprintf (fp, "%d%d", vi0, vi1);
+  vi9 = 0;
+}
+
+/* { dg-final { scan-tree-dump "vi0.*fwrite.*\"hello\".*1, 5, fp.*vi1" "fab"} } */
+/* { dg-final { scan-tree-dump "vi1.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi2" "fab"} } */
+/* { dg-final { scan-tree-dump "vi2.*fputc.*fp.*vi3" "fab"} } */
+/* { dg-final { scan-tree-dump "vi3 = 0\[^\(\)\]*vi4 = 0" "fab"} } */
+/* { dg-final { scan-tree-dump "vi4.*fwrite.*\"hello\".*1, 5, fp.*vi5" "fab"} } */
+/* { dg-final { scan-tree-dump "vi5.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi6" "fab"} } */
+/* { dg-final { scan-tree-dump "vi6.*fputc.*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"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-chk-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-chk-1.c
new file mode 100644 (file)
index 0000000..210bedb
--- /dev/null
@@ -0,0 +1,40 @@
+/* { 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;
+  __fprintf_chk (fp, 1, "hello");
+  vi1 = 0;
+  __fprintf_chk (fp, 1, "hello\n");
+  vi2 = 0;
+  __fprintf_chk (fp, 1, "a");
+  vi3 = 0;
+  __fprintf_chk (fp, 1, "");
+  vi4 = 0;
+  __fprintf_chk (fp, 1, "%s", "hello");
+  vi5 = 0;
+  __fprintf_chk (fp, 1, "%s", "hello\n");
+  vi6 = 0;
+  __fprintf_chk (fp, 1, "%s", "a");
+  vi7 = 0;
+  __fprintf_chk (fp, 1, "%c", 'x');
+  vi8 = 0;
+  __fprintf_chk (fp, 1, "%d%d", vi0, vi1);
+  vi9 = 0;
+}
+
+/* { dg-final { scan-tree-dump "vi0.*fwrite.*\"hello\".*1, 5, fp.*vi1" "fab"} } */
+/* { dg-final { scan-tree-dump "vi1.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi2" "fab"} } */
+/* { dg-final { scan-tree-dump "vi2.*fputc.*fp.*vi3" "fab"} } */
+/* { dg-final { scan-tree-dump "vi3 = 0\[^\(\)\]*vi4 = 0" "fab"} } */
+/* { dg-final { scan-tree-dump "vi4.*fwrite.*\"hello\".*1, 5, fp.*vi5" "fab"} } */
+/* { dg-final { scan-tree-dump "vi5.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi6" "fab"} } */
+/* { dg-final { scan-tree-dump "vi6.*fputc.*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"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-1.c
new file mode 100644 (file)
index 0000000..a984134
--- /dev/null
@@ -0,0 +1,41 @@
+/* { 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;
+  printf ("hello");
+  vi1 = 0;
+  printf ("hello\n");
+  vi2 = 0;
+  printf ("a");
+  vi3 = 0;
+  printf ("");
+  vi4 = 0;
+  printf ("%s", "hello");
+  vi5 = 0;
+  printf ("%s", "hello\n");
+  vi6 = 0;
+  printf ("%s", "a");
+  vi7 = 0;
+  printf ("%s", "");
+  vi8 = 0;
+  printf ("%c", 'x');
+  vi9 = 0;
+  printf ("%s\n", "hello\n");
+  via = 0;
+}
+
+/* { 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 "vi3 = 0\[^\(\)\]*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 "vi7 = 0\[^\(\)\]*vi8 = 0" "fab"} } */
+/* { dg-final { scan-tree-dump "vi8.*putchar.*vi9" "fab"} } */
+/* { dg-final { scan-tree-dump "vi9.*puts.*\"hello\\\\n\".*via" "fab"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-chk-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-chk-1.c
new file mode 100644 (file)
index 0000000..532a3f4
--- /dev/null
@@ -0,0 +1,41 @@
+/* { 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;
+  __printf_chk (1, "hello");
+  vi1 = 0;
+  __printf_chk (1, "hello\n");
+  vi2 = 0;
+  __printf_chk (1, "a");
+  vi3 = 0;
+  __printf_chk (1, "");
+  vi4 = 0;
+  __printf_chk (1, "%s", "hello");
+  vi5 = 0;
+  __printf_chk (1, "%s", "hello\n");
+  vi6 = 0;
+  __printf_chk (1, "%s", "a");
+  vi7 = 0;
+  __printf_chk (1, "%s", "");
+  vi8 = 0;
+  __printf_chk (1, "%c", 'x');
+  vi9 = 0;
+  __printf_chk (1, "%s\n", "hello\n");
+  via = 0;
+}
+
+/* { 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 "vi3 = 0\[^\(\)\]*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 "vi7 = 0\[^\(\)\]*vi8 = 0" "fab"} } */
+/* { dg-final { scan-tree-dump "vi8.*putchar.*vi9" "fab"} } */
+/* { dg-final { scan-tree-dump "vi9.*puts.*\"hello\\\\n\".*via" "fab"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-1.c
new file mode 100644 (file)
index 0000000..d82b311
--- /dev/null
@@ -0,0 +1,38 @@
+/* { 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 ap1, va_list ap2, va_list ap3, va_list ap4, va_list ap5,
+      va_list ap6, va_list ap7)
+{
+  vi0 = 0;
+  vfprintf (fp, "hello", ap1);
+  vi1 = 0;
+  vfprintf (fp, "hello\n", ap2);
+  vi2 = 0;
+  vfprintf (fp, "a", ap3);
+  vi3 = 0;
+  vfprintf (fp, "", ap4);
+  vi4 = 0;
+  vfprintf (fp, "%s", ap5);
+  vi5 = 0;
+  vfprintf (fp, "%c", ap6);
+  vi6 = 0;
+  vfprintf (fp, "%s\n", ap7);
+  vi7 = 0;
+}
+
+/* { dg-final { scan-tree-dump "vi0.*fwrite.*\"hello\".*1, 5, fp.*vi1" "fab"} } */
+/* { dg-final { scan-tree-dump "vi1.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi2" "fab"} } */
+/* { dg-final { scan-tree-dump "vi2.*fputc.*fp.*vi3" "fab"} } */
+/* { dg-final { scan-tree-dump "vi3 = 0\[^\(\)\]*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"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-chk-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-chk-1.c
new file mode 100644 (file)
index 0000000..60a51aa
--- /dev/null
@@ -0,0 +1,38 @@
+/* { 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 ap1, va_list ap2, va_list ap3, va_list ap4, va_list ap5,
+      va_list ap6, va_list ap7)
+{
+  vi0 = 0;
+  __vfprintf_chk (fp, 1, "hello", ap1);
+  vi1 = 0;
+  __vfprintf_chk (fp, 1, "hello\n", ap2);
+  vi2 = 0;
+  __vfprintf_chk (fp, 1, "a", ap3);
+  vi3 = 0;
+  __vfprintf_chk (fp, 1, "", ap4);
+  vi4 = 0;
+  __vfprintf_chk (fp, 1, "%s", ap5);
+  vi5 = 0;
+  __vfprintf_chk (fp, 1, "%c", ap6);
+  vi6 = 0;
+  __vfprintf_chk (fp, 1, "%s\n", ap7);
+  vi7 = 0;
+}
+
+/* { dg-final { scan-tree-dump "vi0.*fwrite.*\"hello\".*1, 5, fp.*vi1" "fab"} } */
+/* { dg-final { scan-tree-dump "vi1.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi2" "fab"} } */
+/* { dg-final { scan-tree-dump "vi2.*fputc.*fp.*vi3" "fab"} } */
+/* { dg-final { scan-tree-dump "vi3 = 0\[^\(\)\]*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"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-1.c
new file mode 100644 (file)
index 0000000..9518105
--- /dev/null
@@ -0,0 +1,36 @@
+/* { 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 ap1, va_list ap2, va_list ap3, va_list ap4, va_list ap5,
+      va_list ap6, va_list ap7)
+{
+  vi0 = 0;
+  vprintf ("hello", ap1);
+  vi1 = 0;
+  vprintf ("hello\n", ap2);
+  vi2 = 0;
+  vprintf ("a", ap3);
+  vi3 = 0;
+  vprintf ("", ap4);
+  vi4 = 0;
+  vprintf ("%s", ap5);
+  vi5 = 0;
+  vprintf ("%c", ap6);
+  vi6 = 0;
+  vprintf ("%s\n", ap7);
+  vi7 = 0;
+}
+
+/* { 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 "vi3 = 0\[^\(\)\]*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"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-chk-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-chk-1.c
new file mode 100644 (file)
index 0000000..d95e336
--- /dev/null
@@ -0,0 +1,36 @@
+/* { 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 ap1, va_list ap2, va_list ap3, va_list ap4, va_list ap5,
+      va_list ap6, va_list ap7)
+{
+  vi0 = 0;
+  __vprintf_chk (1, "hello", ap1);
+  vi1 = 0;
+  __vprintf_chk (1, "hello\n", ap2);
+  vi2 = 0;
+  __vprintf_chk (1, "a", ap3);
+  vi3 = 0;
+  __vprintf_chk (1, "", ap4);
+  vi4 = 0;
+  __vprintf_chk (1, "%s", ap5);
+  vi5 = 0;
+  __vprintf_chk (1, "%c", ap6);
+  vi6 = 0;
+  __vprintf_chk (1, "%s\n", ap7);
+  vi7 = 0;
+}
+
+/* { 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 "vi3 = 0\[^\(\)\]*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"} } */
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
new file mode 100644 (file)
index 0000000..2ba11c0
--- /dev/null
@@ -0,0 +1,1078 @@
+/* __builtin_object_size (ptr, object_size_type) computation
+   Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+   Contributed by Jakub Jelinek <jakub@redhat.com>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "diagnostic.h"
+#include "tree-flow.h"
+#include "tree-pass.h"
+#include "tree-ssa-propagate.h"
+
+struct object_size_info
+{
+  int object_size_type;
+  bitmap visited, reexamine;
+  int pass;
+  bool changed;
+  unsigned int *depths;
+  unsigned int *stack, *tos;
+};
+
+static unsigned HOST_WIDE_INT unknown[4] = { -1, -1, 0, 0 };
+
+static tree compute_object_offset (tree, tree);
+static unsigned HOST_WIDE_INT addr_object_size (tree, int);
+static unsigned HOST_WIDE_INT alloc_object_size (tree, int);
+static tree pass_through_call (tree);
+static void collect_object_sizes_for (struct object_size_info *, tree);
+static void expr_object_size (struct object_size_info *, tree, tree);
+static bool merge_object_sizes (struct object_size_info *, tree, tree,
+                               unsigned HOST_WIDE_INT);
+static bool plus_expr_object_size (struct object_size_info *, tree, tree);
+static void compute_object_sizes (void);
+static void init_offset_limit (void);
+static void check_for_plus_in_loops (struct object_size_info *, tree);
+static void check_for_plus_in_loops_1 (struct object_size_info *, tree,
+                                      unsigned int);
+
+/* object_sizes[0] is upper bound for number of bytes till the end of
+   the object.
+   object_sizes[1] is upper bound for number of bytes till the end of
+   the subobject (innermost array or field with address taken).
+   object_sizes[2] is lower bound for number of bytes till the end of
+   the object and object_sizes[3] lower bound for subobject.  */
+static unsigned HOST_WIDE_INT *object_sizes[4];
+
+/* Bitmaps what object sizes have been computed already.  */
+static bitmap computed[4];
+
+/* Maximum value of offset we consider to be addition.  */
+static unsigned HOST_WIDE_INT offset_limit;
+
+
+/* Initialize OFFSET_LIMIT variable.  */
+static void
+init_offset_limit (void)
+{
+  if (host_integerp (TYPE_MAX_VALUE (sizetype), 1))
+    offset_limit = tree_low_cst (TYPE_MAX_VALUE (sizetype), 1);
+  else
+    offset_limit = -1;
+  offset_limit /= 2;
+}
+
+
+/* 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 for PTR, which is a ADDR_EXPR.
+   OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
+   If unknown, return unknown[object_size_type].  */
+
+static unsigned HOST_WIDE_INT
+addr_object_size (tree ptr, int object_size_type)
+{
+  tree pt_var;
+
+  gcc_assert (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) || TREE_CODE (pt_var) == STRING_CST)
+      && TYPE_SIZE_UNIT (TREE_TYPE (pt_var))
+      && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (pt_var)), 1)
+      && (unsigned HOST_WIDE_INT)
+        tree_low_cst (TYPE_SIZE_UNIT (TREE_TYPE (pt_var)), 1) < offset_limit)
+    {
+      tree bytes;
+
+      if (pt_var != TREE_OPERAND (ptr, 0))
+       {
+         tree var;
+
+         if (object_size_type & 1)
+           {
+             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)
+           {
+             if (TREE_CODE (bytes) == INTEGER_CST
+                 && tree_int_cst_lt (TYPE_SIZE_UNIT (TREE_TYPE (var)), bytes))
+               bytes = size_zero_node;
+             else
+               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);
+    }
+
+  return unknown[object_size_type];
+}
+
+
+/* Compute __builtin_object_size for CALL, which is a CALL_EXPR.
+   Handles various allocation calls.  OBJECT_SIZE_TYPE is the second
+   argument from __builtin_object_size.  If unknown, return
+   unknown[object_size_type].  */
+
+static unsigned HOST_WIDE_INT
+alloc_object_size (tree call, int object_size_type)
+{
+  tree callee, arglist, a, bytes = NULL_TREE;
+  unsigned int arg_mask = 0;
+
+  gcc_assert (TREE_CODE (call) == CALL_EXPR);
+
+  callee = get_callee_fndecl (call);
+  arglist = TREE_OPERAND (call, 1);
+  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;
+      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 unknown[object_size_type];
+}
+
+
+/* If object size is propagated from one of function's arguments directly
+   to its return value, return that argument for CALL_EXPR CALL.
+   Otherwise return NULL.  */
+
+static tree
+pass_through_call (tree call)
+{
+  tree callee = get_callee_fndecl (call);
+  tree arglist = TREE_OPERAND (call, 1);
+
+  if (callee
+      && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL)
+    switch (DECL_FUNCTION_CODE (callee))
+      {
+      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:
+       if (arglist)
+         return TREE_VALUE (arglist);
+       break;
+      default:
+       break;
+      }
+
+  return NULL_TREE;
+}
+
+
+/* Compute __builtin_object_size value for PTR.  OBJECT_SIZE_TYPE is the
+   second argument from __builtin_object_size.  */
+
+unsigned HOST_WIDE_INT
+compute_builtin_object_size (tree ptr, int object_size_type)
+{
+  gcc_assert (object_size_type >= 0 && object_size_type <= 3);
+
+  if (! offset_limit)
+    init_offset_limit ();
+
+  if (TREE_CODE (ptr) == ADDR_EXPR)
+    return addr_object_size (ptr, object_size_type);
+  else if (TREE_CODE (ptr) == CALL_EXPR)
+    {
+      tree arg = pass_through_call (ptr);
+
+      if (arg)
+       return compute_builtin_object_size (arg, object_size_type);
+      else
+       return alloc_object_size (ptr, object_size_type);
+    }
+  else if (TREE_CODE (ptr) == SSA_NAME
+          && POINTER_TYPE_P (TREE_TYPE (ptr))
+          && object_sizes[object_size_type] != NULL)
+    {
+      if (!bitmap_bit_p (computed[object_size_type], SSA_NAME_VERSION (ptr)))
+       {
+         struct object_size_info osi;
+         bitmap_iterator bi;
+         unsigned int i;
+
+         if (dump_file)
+           {
+             fprintf (dump_file, "Computing %s %sobject size for ",
+                      (object_size_type & 2) ? "minimum" : "maximum",
+                      (object_size_type & 1) ? "sub" : "");
+             print_generic_expr (dump_file, ptr, dump_flags);
+             fprintf (dump_file, ":\n");
+           }
+
+         osi.visited = BITMAP_ALLOC (NULL);
+         osi.reexamine = BITMAP_ALLOC (NULL);
+         osi.object_size_type = object_size_type;
+         osi.depths = NULL;
+         osi.stack = NULL;
+         osi.tos = NULL;
+
+         /* First pass: walk UD chains, compute object sizes that
+            can be computed.  osi.reexamine bitmap at the end will
+            contain what variables were found in dependency cycles
+            and therefore need to be reexamined.  */
+         osi.pass = 0;
+         osi.changed = false;
+         collect_object_sizes_for (&osi, ptr);
+
+         /* Second pass: keep recomputing object sizes of variables
+            that need reexamination, until no object sizes are
+            increased or all object sizes are computed.  */
+         if (! bitmap_empty_p (osi.reexamine))
+           {
+             bitmap reexamine = BITMAP_ALLOC (NULL);
+
+             /* If looking for minimum instead of maximum object size,
+                detect cases where a pointer is increased in a loop.
+                Although even without this detection pass 2 would eventually
+                terminate, it could take a long time.  If a pointer is
+                increasing this way, we need to assume 0 object size.
+                E.g. p = &buf[0]; while (cond) p = p + 4;  */
+             if (object_size_type & 2)
+               {
+                 osi.depths = xcalloc (num_ssa_names, sizeof (unsigned int));
+                 osi.stack = xmalloc (num_ssa_names * sizeof (unsigned int));
+                 osi.tos = osi.stack;
+                 osi.pass = 1;
+                 /* collect_object_sizes_for is changing
+                    osi.reexamine bitmap, so iterate over a copy.  */
+                 bitmap_copy (reexamine, osi.reexamine);
+                 EXECUTE_IF_SET_IN_BITMAP (reexamine, 0, i, bi)
+                   if (bitmap_bit_p (osi.reexamine, i))
+                     check_for_plus_in_loops (&osi, ssa_name (i));
+
+                 free (osi.depths);
+                 osi.depths = NULL;
+                 free (osi.stack);
+                 osi.stack = NULL;
+                 osi.tos = NULL;
+               }
+
+             do
+               {
+                 osi.pass = 2;
+                 osi.changed = false;
+                 /* collect_object_sizes_for is changing
+                    osi.reexamine bitmap, so iterate over a copy.  */
+                 bitmap_copy (reexamine, osi.reexamine);
+                 EXECUTE_IF_SET_IN_BITMAP (reexamine, 0, i, bi)
+                   if (bitmap_bit_p (osi.reexamine, i))
+                     {
+                       collect_object_sizes_for (&osi, ssa_name (i));
+                       if (dump_file && (dump_flags & TDF_DETAILS))
+                         {
+                           fprintf (dump_file, "Reexamining ");
+                           print_generic_expr (dump_file, ssa_name (i),
+                                               dump_flags);
+                           fprintf (dump_file, "\n");
+                         }
+                     }
+               }
+             while (osi.changed);
+
+             BITMAP_FREE (reexamine);
+           }
+         EXECUTE_IF_SET_IN_BITMAP (osi.reexamine, 0, i, bi)
+           bitmap_set_bit (computed[object_size_type], i);
+
+         /* Debugging dumps.  */
+         if (dump_file)
+           {
+             EXECUTE_IF_SET_IN_BITMAP (osi.visited, 0, i, bi)
+               if (object_sizes[object_size_type][i]
+                   != unknown[object_size_type])
+                 {
+                   print_generic_expr (dump_file, ssa_name (i),
+                                       dump_flags);
+                   fprintf (dump_file,
+                            ": %s %sobject size "
+                            HOST_WIDE_INT_PRINT_UNSIGNED "\n",
+                            (object_size_type & 2) ? "minimum" : "maximum",
+                            (object_size_type & 1) ? "sub" : "",
+                            object_sizes[object_size_type][i]);
+                 }
+           }
+
+         BITMAP_FREE (osi.reexamine);
+         BITMAP_FREE (osi.visited);
+       }
+
+      return object_sizes[object_size_type][SSA_NAME_VERSION (ptr)];
+    }
+
+  return unknown[object_size_type];
+}
+
+
+/* Compute object_sizes for PTR, defined to VALUE, which is not
+   a SSA_NAME.  */
+
+static void
+expr_object_size (struct object_size_info *osi, tree ptr, tree value)
+{
+  int object_size_type = osi->object_size_type;
+  unsigned int varno = SSA_NAME_VERSION (ptr);
+  unsigned HOST_WIDE_INT bytes;
+
+  gcc_assert (object_sizes[object_size_type][varno]
+             != unknown[object_size_type]);
+  gcc_assert (osi->pass == 0);
+
+  if (TREE_CODE (value) == WITH_SIZE_EXPR)
+    value = TREE_OPERAND (value, 0);
+
+  /* Pointer variables should have been handled by merge_object_sizes.  */
+  gcc_assert (TREE_CODE (value) != SSA_NAME
+             || !POINTER_TYPE_P (TREE_TYPE (value)));
+
+  if (TREE_CODE (value) == ADDR_EXPR)
+    bytes = addr_object_size (value, object_size_type);
+  else if (TREE_CODE (value) == CALL_EXPR)
+    bytes = alloc_object_size (value, object_size_type);
+  else
+    bytes = unknown[object_size_type];
+
+  if ((object_size_type & 2) == 0)
+    {
+      if (object_sizes[object_size_type][varno] < bytes)
+       object_sizes[object_size_type][varno] = bytes;
+    }
+  else
+    {
+      if (object_sizes[object_size_type][varno] > bytes)
+       object_sizes[object_size_type][varno] = bytes;
+    }
+}
+
+
+/* Merge object sizes of ORIG + OFFSET into DEST.  Return true if
+   the object size might need reexamination later.  */
+
+static bool
+merge_object_sizes (struct object_size_info *osi, tree dest, tree orig,
+                   unsigned HOST_WIDE_INT offset)
+{
+  int object_size_type = osi->object_size_type;
+  unsigned int varno = SSA_NAME_VERSION (dest);
+  unsigned HOST_WIDE_INT orig_bytes;
+
+  if (object_sizes[object_size_type][varno] == unknown[object_size_type])
+    return false;
+  if (offset >= offset_limit)
+    {
+      object_sizes[object_size_type][varno] = unknown[object_size_type];
+      return false;
+    }
+
+  if (osi->pass == 0)
+    collect_object_sizes_for (osi, orig);
+
+  orig_bytes = object_sizes[object_size_type][SSA_NAME_VERSION (orig)];
+  if (orig_bytes != unknown[object_size_type])
+    orig_bytes = (offset > orig_bytes)
+                ? (unsigned HOST_WIDE_INT) 0 : orig_bytes - offset;
+
+  if ((object_size_type & 2) == 0)
+    {
+      if (object_sizes[object_size_type][varno] < orig_bytes)
+       {
+         object_sizes[object_size_type][varno] = orig_bytes;
+         osi->changed = true;
+       }
+    }
+  else
+    {
+      if (object_sizes[object_size_type][varno] > orig_bytes)
+       {
+         object_sizes[object_size_type][varno] = orig_bytes;
+         osi->changed = true;
+       }
+    }
+  return bitmap_bit_p (osi->reexamine, SSA_NAME_VERSION (orig));
+}
+
+
+/* Compute object_sizes for PTR, defined to VALUE, which is
+   a PLUS_EXPR.  Return true if the object size might need reexamination
+   later.  */
+
+static bool
+plus_expr_object_size (struct object_size_info *osi, 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;
+  int object_size_type = osi->object_size_type;
+  unsigned int varno = SSA_NAME_VERSION (var);
+  unsigned HOST_WIDE_INT bytes;
+
+  gcc_assert (TREE_CODE (value) == PLUS_EXPR);
+
+  if (object_sizes[object_size_type][varno] == unknown[object_size_type])
+    return false;
+
+  /* Swap operands if needed.  */
+  if (ptr2_p && !ptr1_p)
+    {
+      tree tem = op0;
+      op0 = op1;
+      op1 = tem;
+      ptr1_p = true;
+      ptr2_p = false;
+    }
+
+  /* Handle PTR + OFFSET here.  */
+  if (ptr1_p
+      && !ptr2_p
+      && TREE_CODE (op1) == INTEGER_CST
+      && (TREE_CODE (op0) == SSA_NAME
+         || TREE_CODE (op0) == ADDR_EXPR))
+    {
+      if (! host_integerp (op1, 1))
+       bytes = unknown[object_size_type];
+      else if (TREE_CODE (op0) == SSA_NAME)
+       return merge_object_sizes (osi, var, op0, tree_low_cst (op1, 1));
+      else
+       {
+         unsigned HOST_WIDE_INT off = tree_low_cst (op1, 1);
+
+         bytes = compute_builtin_object_size (value, object_size_type);
+         if (off > offset_limit)
+           bytes = unknown[object_size_type];
+         else if (off > bytes)
+           bytes = 0;
+         else
+           bytes -= off;
+       }
+    }
+  else
+    bytes = unknown[object_size_type];
+
+  if ((object_size_type & 2) == 0)
+    {
+      if (object_sizes[object_size_type][varno] < bytes)
+       object_sizes[object_size_type][varno] = bytes;
+    }
+  else
+    {
+      if (object_sizes[object_size_type][varno] > bytes)
+       object_sizes[object_size_type][varno] = bytes;
+    }
+  return false;
+}
+
+
+/* Compute object sizes for VAR.
+   For ADDR_EXPR an object size is the number of remaining bytes
+   to the end of the object (where what is consindered an object depends on
+   OSI->object_size_type).
+   For allocation CALL_EXPR like malloc or calloc object size is the size
+   of the allocation.
+   For pointer PLUS_EXPR where second operand is a constant integer,
+   object size is object size of the first operand minus the constant.
+   If the constant is bigger than the number of remaining bytes until the
+   end of the object, object size is 0, but if it is instead a pointer
+   subtraction, object size is unknown[object_size_type].
+   To differentiate addition from subtraction, ADDR_EXPR returns
+   unknown[object_size_type] for all objects bigger than half of the address
+   space, and constants less than half of the address space are considered
+   addition, while bigger constants subtraction.
+   For a memcpy like CALL_EXPR that always returns one of its arguments, the
+   object size is object size of that argument.
+   Otherwise, object size is the maximum of object sizes of variables
+   that it might be set to.  */
+
+static void
+collect_object_sizes_for (struct object_size_info *osi, tree var)
+{
+  int object_size_type = osi->object_size_type;
+  unsigned int varno = SSA_NAME_VERSION (var);
+  tree stmt;
+  bool reexamine;
+
+  if (bitmap_bit_p (computed[object_size_type], varno))
+    return;
+
+  if (osi->pass == 0)
+    {
+      if (! bitmap_bit_p (osi->visited, varno))
+       {
+         bitmap_set_bit (osi->visited, varno);
+         object_sizes[object_size_type][varno]
+           = (object_size_type & 2) ? -1 : 0;
+       }
+      else
+       {
+         /* Found a dependency loop.  Mark the variable for later
+            re-examination.  */
+         bitmap_set_bit (osi->reexamine, varno);
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           {
+             fprintf (dump_file, "Found a dependency loop at ");
+             print_generic_expr (dump_file, var, dump_flags);
+             fprintf (dump_file, "\n");
+           }
+         return;
+       }
+    }
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "Visiting use-def links for ");
+      print_generic_expr (dump_file, var, dump_flags);
+      fprintf (dump_file, "\n");
+    }
+
+  stmt = SSA_NAME_DEF_STMT (var);
+  reexamine = false;
+
+  switch (TREE_CODE (stmt))
+    {
+    case RETURN_EXPR:
+      if (TREE_CODE (TREE_OPERAND (stmt, 0)) != MODIFY_EXPR)
+       abort ();
+      stmt = TREE_OPERAND (stmt, 0);
+      /* FALLTHRU  */
+
+    case MODIFY_EXPR:
+      {
+       tree rhs = TREE_OPERAND (stmt, 1), arg;
+       STRIP_NOPS (rhs);
+
+       if (TREE_CODE (rhs) == CALL_EXPR)
+         {
+           arg = pass_through_call (rhs);
+           if (arg)
+             rhs = arg;
+         }
+
+       if (TREE_CODE (rhs) == SSA_NAME
+           && POINTER_TYPE_P (TREE_TYPE (rhs)))
+         reexamine = merge_object_sizes (osi, var, rhs, 0);
+
+       else if (TREE_CODE (rhs) == PLUS_EXPR)
+         reexamine = plus_expr_object_size (osi, var, rhs);
+
+       else
+         expr_object_size (osi, var, rhs);
+       break;
+      }
+
+    case ASM_EXPR:
+      /* Pointers defined by __asm__ statements can point anywhere.  */
+      object_sizes[object_size_type][varno] = unknown[object_size_type];
+      break;
+
+    case NOP_EXPR:
+      {
+       tree decl = SSA_NAME_VAR (var);
+
+       gcc_assert (IS_EMPTY_STMT (stmt));
+
+       if (TREE_CODE (decl) != PARM_DECL && DECL_INITIAL (decl))
+         expr_object_size (osi, var, DECL_INITIAL (decl));
+       else
+         expr_object_size (osi, var, decl);
+      }
+      break;
+
+    case PHI_NODE:
+      {
+       int i;
+
+       for (i = 0; i < PHI_NUM_ARGS (stmt); i++)
+         {
+           tree rhs = PHI_ARG_DEF (stmt, i);
+
+           if (object_sizes[object_size_type][varno]
+               == unknown[object_size_type])
+             break;
+
+           if (TREE_CODE (rhs) == SSA_NAME)
+             reexamine |= merge_object_sizes (osi, var, rhs, 0);
+           else if (osi->pass == 0)
+             expr_object_size (osi, var, rhs);
+         }
+       break;
+      }
+    default:
+      gcc_unreachable ();
+    }
+
+  if (! reexamine
+      || object_sizes[object_size_type][varno] == unknown[object_size_type])
+    {
+      bitmap_set_bit (computed[object_size_type], varno);
+      bitmap_clear_bit (osi->reexamine, varno);
+    }
+  else
+    {
+      bitmap_set_bit (osi->reexamine, varno);
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       {
+         fprintf (dump_file, "Need to reexamine ");
+         print_generic_expr (dump_file, var, dump_flags);
+         fprintf (dump_file, "\n");
+       }
+    }
+}
+
+
+/* Helper function for check_for_plus_in_loops.  Called recursively
+   to detect loops.  */
+
+static void
+check_for_plus_in_loops_1 (struct object_size_info *osi, tree var,
+                          unsigned int depth)
+{
+  tree stmt = SSA_NAME_DEF_STMT (var);
+  unsigned int varno = SSA_NAME_VERSION (var);
+
+  if (osi->depths[varno])
+    {
+      if (osi->depths[varno] != depth)
+       {
+         unsigned int *sp;
+
+         /* Found a loop involving pointer addition.  */
+         for (sp = osi->tos; sp > osi->stack; )
+           {
+             --sp;
+             bitmap_clear_bit (osi->reexamine, *sp);
+             bitmap_set_bit (computed[osi->object_size_type], *sp);
+             object_sizes[osi->object_size_type][*sp] = 0;
+             if (*sp == varno)
+               break;
+           }
+       }
+      return;
+    }
+  else if (! bitmap_bit_p (osi->reexamine, varno))
+    return;
+
+  osi->depths[varno] = depth;
+  *osi->tos++ = varno;
+
+  switch (TREE_CODE (stmt))
+    {
+    case RETURN_EXPR:
+      if (TREE_CODE (TREE_OPERAND (stmt, 0)) != MODIFY_EXPR)
+       abort ();
+      stmt = TREE_OPERAND (stmt, 0);
+      /* FALLTHRU  */
+
+    case MODIFY_EXPR:
+      {
+       tree rhs = TREE_OPERAND (stmt, 1), arg;
+       STRIP_NOPS (rhs);
+
+       if (TREE_CODE (rhs) == CALL_EXPR)
+         {
+           arg = pass_through_call (rhs);
+           if (arg)
+             rhs = arg;
+         }
+
+       if (TREE_CODE (rhs) == SSA_NAME)
+         check_for_plus_in_loops_1 (osi, rhs, depth);
+       else if (TREE_CODE (rhs) == PLUS_EXPR)
+         {
+           tree op0 = TREE_OPERAND (rhs, 0);
+           tree op1 = TREE_OPERAND (rhs, 1);
+           tree cst, basevar;
+
+           if (TREE_CODE (op0) == SSA_NAME)
+             {
+               basevar = op0;
+               cst = op1;
+             }
+           else
+             {
+               basevar = op1;
+               cst = op0;
+               gcc_assert (TREE_CODE (basevar) == SSA_NAME);
+             }
+           gcc_assert (TREE_CODE (cst) == INTEGER_CST);
+
+           check_for_plus_in_loops_1 (osi, basevar,
+                                      depth + !integer_zerop (cst));
+         }
+       else
+         gcc_unreachable ();
+       break;
+      }
+    case PHI_NODE:
+      {
+       int i;
+
+       for (i = 0; i < PHI_NUM_ARGS (stmt); i++)
+         {
+           tree rhs = PHI_ARG_DEF (stmt, i);
+
+           if (TREE_CODE (rhs) == SSA_NAME)
+             check_for_plus_in_loops_1 (osi, rhs, depth);
+         }
+       break;
+      }
+    default:
+      gcc_unreachable ();
+    }
+
+  osi->depths[varno] = 0;
+  osi->tos--;
+}
+
+
+/* Check if some pointer we are computing object size of is being increased
+   within a loop.  If yes, assume all the SSA variables participating in
+   that loop have minimum object sizes 0.  */
+
+static void
+check_for_plus_in_loops (struct object_size_info *osi, tree var)
+{
+  tree stmt = SSA_NAME_DEF_STMT (var);
+
+  switch (TREE_CODE (stmt))
+    {
+    case RETURN_EXPR:
+      if (TREE_CODE (TREE_OPERAND (stmt, 0)) != MODIFY_EXPR)
+       abort ();
+      stmt = TREE_OPERAND (stmt, 0);
+      /* FALLTHRU  */
+
+    case MODIFY_EXPR:
+      {
+       tree rhs = TREE_OPERAND (stmt, 1), arg;
+       STRIP_NOPS (rhs);
+
+       if (TREE_CODE (rhs) == CALL_EXPR)
+         {
+           arg = pass_through_call (rhs);
+           if (arg)
+             rhs = arg;
+         }
+
+       if (TREE_CODE (rhs) == PLUS_EXPR)
+         {
+           tree op0 = TREE_OPERAND (rhs, 0);
+           tree op1 = TREE_OPERAND (rhs, 1);
+           tree cst, basevar;
+
+           if (TREE_CODE (op0) == SSA_NAME)
+             {
+               basevar = op0;
+               cst = op1;
+             }
+           else
+             {
+               basevar = op1;
+               cst = op0;
+               gcc_assert (TREE_CODE (basevar) == SSA_NAME);
+             }
+           gcc_assert (TREE_CODE (cst) == INTEGER_CST);
+
+           if (integer_zerop (cst))
+             break;
+
+           osi->depths[SSA_NAME_VERSION (basevar)] = 1;
+           *osi->tos++ = SSA_NAME_VERSION (basevar);
+           check_for_plus_in_loops_1 (osi, var, 2);
+           osi->depths[SSA_NAME_VERSION (basevar)] = 0;
+           osi->tos--;
+         }
+       break;
+      }
+    default:
+      break;
+    }
+}
+
+
+/* Initialize data structures for the object size computation.  */
+
+void
+init_object_sizes (void)
+{
+  int object_size_type;
+
+  if (object_sizes[0])
+    return;
+
+  for (object_size_type = 0; object_size_type <= 3; object_size_type++)
+    {
+      object_sizes[object_size_type]
+       = xmalloc (num_ssa_names * sizeof (HOST_WIDE_INT));
+      computed[object_size_type] = BITMAP_ALLOC (NULL);
+    }
+
+  init_offset_limit ();
+}
+
+
+/* Destroy data structures after the object size computation.  */
+
+void
+fini_object_sizes (void)
+{
+  int object_size_type;
+
+  for (object_size_type = 0; object_size_type <= 3; object_size_type++)
+    {
+      free (object_sizes[object_size_type]);
+      BITMAP_FREE (computed[object_size_type]);
+      object_sizes[object_size_type] = NULL;
+    }
+}
+
+
+/* Simple pass to optimize all __builtin_object_size () builtins.  */
+
+static void
+compute_object_sizes (void)
+{
+  basic_block bb;
+  FOR_EACH_BB (bb)
+    {
+      block_stmt_iterator i;
+      for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i))
+       {
+         tree *stmtp = bsi_stmt_ptr (i);
+         tree call = get_rhs (*stmtp);
+         tree callee, result;
+
+         if (!call || TREE_CODE (call) != CALL_EXPR)
+           continue;
+
+         callee = get_callee_fndecl (call);
+         if (!callee
+             || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL
+             || DECL_FUNCTION_CODE (callee) != BUILT_IN_OBJECT_SIZE)
+           continue;
+
+         init_object_sizes ();
+         result = fold_builtin (callee, TREE_OPERAND (call, 1), false);
+         if (!result)
+           {
+             tree arglist = TREE_OPERAND (call, 1);
+
+             if (arglist != NULL
+                 && POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
+                 && TREE_CHAIN (arglist) != NULL
+                 && TREE_CHAIN (TREE_CHAIN (arglist)) == NULL)
+               {
+                 tree ost = TREE_VALUE (TREE_CHAIN (arglist));
+
+                 if (host_integerp (ost, 1))
+                   {
+                     unsigned HOST_WIDE_INT object_size_type
+                       = tree_low_cst (ost, 1);
+
+                     if (object_size_type < 2)
+                       result = fold_convert (size_type_node,
+                                              integer_minus_one_node);
+                     else if (object_size_type < 4)
+                       result = size_zero_node;
+                   }
+               }
+
+             if (!result)
+               continue;
+           }
+
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           {
+             fprintf (dump_file, "Simplified\n  ");
+             print_generic_stmt (dump_file, *stmtp, dump_flags);
+           }
+
+         if (!set_rhs (stmtp, result))
+           abort ();
+         update_stmt (*stmtp);
+
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           {
+             fprintf (dump_file, "to\n  ");
+             print_generic_stmt (dump_file, *stmtp, dump_flags);
+             fprintf (dump_file, "\n");
+           }
+       }
+    }
+
+  fini_object_sizes ();
+}
+
+struct tree_opt_pass pass_object_sizes =
+{
+  "objsz",                             /* name */
+  NULL,                                        /* gate */
+  compute_object_sizes,                        /* execute */
+  NULL,                                        /* sub */
+  NULL,                                        /* next */
+  0,                                   /* static_pass_number */
+  0,                                   /* tv_id */
+  PROP_cfg | PROP_ssa | PROP_alias,    /* properties_required */
+  0,                                   /* properties_provided */
+  0,                                   /* properties_destroyed */
+  0,                                   /* todo_flags_start */
+  TODO_dump_func | TODO_verify_ssa,    /* todo_flags_finish */
+  0                                    /* letter */
+};
index 0e477de528faeafa598f8a2e285d5a69034f77d4..4b46c9b7ec5f6320b84496ba53883c4c72282ac7 100644 (file)
@@ -435,6 +435,7 @@ init_tree_optimization_passes (void)
   NEXT_PASS (pass_may_alias);
   NEXT_PASS (pass_forwprop);
   NEXT_PASS (pass_phiopt);
+  NEXT_PASS (pass_object_sizes);
   NEXT_PASS (pass_store_ccp);
   NEXT_PASS (pass_store_copy_prop);
   NEXT_PASS (pass_fold_builtins);
index d6eaaa997da8dc81cd439bfa04458a762c42b17e..471747bf73ca76f90fae4db317188bfb54f0f13e 100644 (file)
@@ -196,6 +196,7 @@ extern struct tree_opt_pass pass_lower_complex_O0;
 extern struct tree_opt_pass pass_lower_complex;
 extern struct tree_opt_pass pass_lower_vector;
 extern struct tree_opt_pass pass_lower_vector_ssa;
+extern struct tree_opt_pass pass_object_sizes;
 extern struct tree_opt_pass pass_fold_builtins;
 extern struct tree_opt_pass pass_stdarg;
 extern struct tree_opt_pass pass_early_warn_uninitialized;
index 71385ddeffdc70af4e5da76858282807575b1bc6..8bf824250e4871b17db33fd50db1a499bbbe814a 100644 (file)
@@ -1965,24 +1965,49 @@ fold_stmt_r (tree *expr_p, int *walk_subtrees, void *data)
 }
 
 
-/* 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;
@@ -2000,28 +2025,14 @@ get_strlen (tree arg, tree *length, bitmap visited)
     {
       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:
@@ -2043,7 +2054,7 @@ get_strlen (tree arg, tree *length, bitmap visited)
              if (arg == PHI_RESULT (def_stmt))
                continue;
 
-             if (!get_strlen (arg, length, visited))
+             if (!get_maxval_strlen (arg, length, visited, type))
                return false;
            }
 
@@ -2065,9 +2076,9 @@ get_strlen (tree arg, tree *length, bitmap visited)
 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;
 
@@ -2079,11 +2090,11 @@ ccp_fold_builtin (tree stmt, tree fn)
   arglist = TREE_OPERAND (fn, 1);
   result = fold_builtin (callee, arglist, ignore);
   if (result)
-  {
-    if (ignore)
-      STRIP_NOPS (result);
-    return result;
-  }
+    {
+      if (ignore)
+       STRIP_NOPS (result);
+      return result;
+    }
 
   /* Ignore MD builtins.  */
   if (DECL_BUILT_IN_CLASS (callee) == BUILT_IN_MD)
@@ -2100,11 +2111,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;
@@ -2113,15 +2144,15 @@ ccp_fold_builtin (tree stmt, tree fn)
   /* Try to use the dataflow information gathered by the CCP process.  */
   visited = BITMAP_ALLOC (NULL);
 
-  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_FREE (visited);
@@ -2130,9 +2161,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.  */
@@ -2144,33 +2175,53 @@ ccp_fold_builtin (tree stmt, tree fn)
       break;
 
     case BUILT_IN_STRCPY:
-      if (strlen_val[1] && is_gimple_val (strlen_val[1]))
-       {
-         tree fndecl = get_callee_fndecl (fn);
-         tree arglist = TREE_OPERAND (fn, 1);
-         result = fold_builtin_strcpy (fndecl, arglist, strlen_val[1]);
-       }
+      if (val[1] && is_gimple_val (val[1]))
+       result = fold_builtin_strcpy (callee, arglist, val[1]);
       break;
 
     case BUILT_IN_STRNCPY:
-      if (strlen_val[1] && is_gimple_val (strlen_val[1]))
-       {
-         tree fndecl = get_callee_fndecl (fn);
-         tree arglist = TREE_OPERAND (fn, 1);
-         result = fold_builtin_strncpy (fndecl, arglist, strlen_val[1]);
-       }
+      if (val[1] && is_gimple_val (val[1]))
+       result = fold_builtin_strncpy (callee, arglist, 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 (callee, arglist, 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 (callee, arglist, 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 (arglist, 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:
@@ -2338,18 +2389,26 @@ 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 old_stmt = *stmtp;
          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)
@@ -2363,6 +2422,7 @@ execute_fold_all_builtins (void)
                break;
 
              default:
+               bsi_next (&i);
                continue;
              }
 
@@ -2393,6 +2453,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);
        }
     }
 
index 1c0a4be2fcb9f24c8669fed5ac1548a27ffbb4b7..a2e9a86f886a52346aef922d429ddb3e669a7f15 100644 (file)
@@ -3623,6 +3623,12 @@ extern tree fold_builtin (tree, tree, bool);
 extern tree fold_builtin_fputs (tree, bool, bool, tree);
 extern tree fold_builtin_strcpy (tree, tree, tree);
 extern tree fold_builtin_strncpy (tree, tree, tree);
+extern tree fold_builtin_memory_chk (tree, tree, tree, bool,
+                                    enum built_in_function);
+extern tree fold_builtin_stxcpy_chk (tree, 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 bool fold_builtin_next_arg (tree);
 extern enum built_in_function builtin_mathfn_code (tree);
 extern tree build_function_call_expr (tree, tree);
@@ -4010,4 +4016,9 @@ extern void vect_set_verbosity_level (const char *);
 extern tree tree_mem_ref_addr (tree, tree);
 extern void copy_mem_ref_info (tree, tree);
 
+/* In tree-object-size.c.  */
+extern void init_object_sizes (void);
+extern void fini_object_sizes (void);
+extern unsigned HOST_WIDE_INT compute_builtin_object_size (tree, int);
+
 #endif  /* GCC_TREE_H  */