2005-06-21 Jakub Jelinek * 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. * 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. --- gcc/builtin-types.def.jj 2005-05-06 10:05:56.000000000 +0200 +++ gcc/builtin-types.def 2005-06-21 13:45:58.000000000 +0200 @@ -32,6 +32,7 @@ Software Foundation, 59 Temple Place - S DEF_FUNCTION_TYPE_2 (ENUM, RETURN, ARG1, ARG2) DEF_FUNCTION_TYPE_3 (ENUM, RETURN, ARG1, ARG2, ARG3) DEF_FUNCTION_TYPE_4 (ENUM, RETURN, ARG1, ARG2, ARG3, ARG4) + DEF_FUNCTION_TYPE_5 (ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5) These macros describe function types. ENUM is as above. The RETURN type is one of the enumerals already defined. ARG1, ARG2, @@ -41,6 +42,7 @@ Software Foundation, 59 Temple Place - S DEF_FUNCTION_TYPE_VAR_1 (ENUM, RETURN, ARG1) DEF_FUNCTION_TYPE_VAR_2 (ENUM, RETURN, ARG1, ARG2) DEF_FUNCTION_TYPE_VAR_3 (ENUM, RETURN, ARG1, ARG2, ARG3) + DEF_FUNCTION_TYPE_VAR_4 (ENUM, RETURN, ARG1, ARG2, ARG3, ARG4) Similar, but for function types that take variable arguments. For example: @@ -252,6 +254,7 @@ DEF_FUNCTION_TYPE_2 (BT_FN_COMPLEX_LONGD DEF_FUNCTION_TYPE_2 (BT_FN_VOID_PTR_PTR, BT_VOID, BT_PTR, BT_PTR) DEF_FUNCTION_TYPE_2 (BT_FN_INT_CONST_STRING_PTR_CONST_STRING, BT_INT, BT_CONST_STRING, BT_PTR_CONST_STRING) +DEF_FUNCTION_TYPE_2 (BT_FN_SIZE_CONST_PTR_INT, BT_SIZE, BT_CONST_PTR, BT_INT) DEF_FUNCTION_TYPE_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_LONGDOUB DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_PTR_PTR, BT_VOID, BT_PTR, BT_PTR, BT_PTR) DEF_FUNCTION_TYPE_3 (BT_FN_INT_CONST_STRING_PTR_CONST_STRING_PTR_CONST_STRING, BT_INT, BT_CONST_STRING, BT_PTR_CONST_STRING, BT_PTR_CONST_STRING) +DEF_FUNCTION_TYPE_3 (BT_FN_INT_INT_CONST_STRING_VALIST_ARG, + BT_INT, BT_INT, BT_CONST_STRING, BT_VALIST_ARG) DEF_FUNCTION_TYPE_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_SI BT_INT, BT_STRING, BT_SIZE, BT_CONST_STRING, BT_VALIST_ARG) DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_STRING_SIZE_CONST_STRING_CONST_PTR, BT_SIZE, BT_STRING, BT_SIZE, BT_CONST_STRING, BT_CONST_PTR) +DEF_FUNCTION_TYPE_4 (BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, + BT_PTR, BT_PTR, BT_CONST_PTR, BT_SIZE, BT_SIZE) +DEF_FUNCTION_TYPE_4 (BT_FN_PTR_PTR_INT_SIZE_SIZE, + BT_PTR, BT_PTR, BT_INT, BT_SIZE, BT_SIZE) +DEF_FUNCTION_TYPE_4 (BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, + BT_STRING, BT_STRING, BT_CONST_STRING, BT_SIZE, BT_SIZE) +DEF_FUNCTION_TYPE_4 (BT_FN_INT_FILEPTR_INT_CONST_STRING_VALIST_ARG, + BT_INT, BT_FILEPTR, BT_INT, BT_CONST_STRING, BT_VALIST_ARG) + +DEF_FUNCTION_TYPE_5 (BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VALIST_ARG, + BT_INT, BT_STRING, BT_INT, BT_SIZE, BT_CONST_STRING, + BT_VALIST_ARG) + +DEF_FUNCTION_TYPE_6 (BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VALIST_ARG, + BT_INT, BT_STRING, BT_SIZE, BT_INT, BT_SIZE, + BT_CONST_STRING, BT_VALIST_ARG) DEF_FUNCTION_TYPE_VAR_0 (BT_FN_VOID_VAR, BT_VOID) DEF_FUNCTION_TYPE_VAR_0 (BT_FN_INT_VAR, BT_INT) @@ -337,11 +358,22 @@ DEF_FUNCTION_TYPE_VAR_2 (BT_FN_INT_STRIN BT_INT, BT_STRING, BT_CONST_STRING) DEF_FUNCTION_TYPE_VAR_2 (BT_FN_INT_CONST_STRING_CONST_STRING_VAR, BT_INT, BT_CONST_STRING, BT_CONST_STRING) +DEF_FUNCTION_TYPE_VAR_2 (BT_FN_INT_INT_CONST_STRING_VAR, + BT_INT, BT_INT, BT_CONST_STRING) DEF_FUNCTION_TYPE_VAR_3 (BT_FN_INT_STRING_SIZE_CONST_STRING_VAR, BT_INT, BT_STRING, BT_SIZE, BT_CONST_STRING) DEF_FUNCTION_TYPE_VAR_3 (BT_FN_SSIZE_STRING_SIZE_CONST_STRING_VAR, BT_SSIZE, BT_STRING, BT_SIZE, BT_CONST_STRING) +DEF_FUNCTION_TYPE_VAR_3 (BT_FN_INT_FILEPTR_INT_CONST_STRING_VAR, + BT_INT, BT_FILEPTR, BT_INT, BT_CONST_STRING) + +DEF_FUNCTION_TYPE_VAR_4 (BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VAR, + BT_INT, BT_STRING, BT_INT, BT_SIZE, BT_CONST_STRING) + +DEF_FUNCTION_TYPE_VAR_5 (BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VAR, + BT_INT, BT_STRING, BT_SIZE, BT_INT, BT_SIZE, + BT_CONST_STRING) DEF_POINTER_TYPE (BT_PTR_FN_VOID_VAR, BT_FN_VOID_VAR) DEF_FUNCTION_TYPE_3 (BT_FN_PTR_PTR_FN_VOID_VAR_PTR_SIZE, --- gcc/builtins.c.jj 2005-06-18 01:07:21.000000000 +0200 +++ gcc/builtins.c 2005-06-21 13:45:58.000000000 +0200 @@ -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 tar } /* 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, tr } /* 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, rt 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 arglis 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 '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 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); +} --- gcc/tree.h.jj 2005-06-18 01:07:30.000000000 +0200 +++ gcc/tree.h 2005-06-21 13:45:58.000000000 +0200 @@ -3623,6 +3623,12 @@ extern tree fold_builtin (tree, tree, bo 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); @@ -4009,4 +4015,9 @@ extern void vect_set_verbosity_level (co 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 */ --- gcc/tree-pass.h.jj 2005-06-18 01:07:28.000000000 +0200 +++ gcc/tree-pass.h 2005-06-21 13:45:58.000000000 +0200 @@ -196,6 +196,7 @@ extern struct tree_opt_pass pass_lower_c 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; --- gcc/builtins.def.jj 2005-06-07 12:30:17.000000000 +0200 +++ gcc/builtins.def 2005-06-21 13:45:58.000000000 +0200 @@ -658,6 +658,26 @@ DEF_BUILTIN_STUB (BUILT_IN_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") --- gcc/c-common.c.jj 2005-06-18 01:07:21.000000000 +0200 +++ gcc/c-common.c 2005-06-21 13:45:58.000000000 +0200 @@ -2919,10 +2919,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 @@ -2931,10 +2937,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 }; @@ -3183,6 +3193,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); @@ -3215,6 +3261,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]); @@ -3224,10 +3302,14 @@ c_common_nodes_and_builtins (void) #undef DEF_FUNCTION_TYPE_2 #undef DEF_FUNCTION_TYPE_3 #undef DEF_FUNCTION_TYPE_4 +#undef DEF_FUNCTION_TYPE_5 +#undef DEF_FUNCTION_TYPE_6 #undef DEF_FUNCTION_TYPE_VAR_0 #undef DEF_FUNCTION_TYPE_VAR_1 #undef DEF_FUNCTION_TYPE_VAR_2 #undef DEF_FUNCTION_TYPE_VAR_3 +#undef DEF_FUNCTION_TYPE_VAR_4 +#undef DEF_FUNCTION_TYPE_VAR_5 #undef DEF_POINTER_TYPE c_init_attributes (); --- gcc/tree-optimize.c.jj 2005-06-18 01:07:28.000000000 +0200 +++ gcc/tree-optimize.c 2005-06-21 13:45:58.000000000 +0200 @@ -434,6 +434,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); --- gcc/tree-object-size.c.jj 2005-06-21 13:45:58.000000000 +0200 +++ gcc/tree-object-size.c 2005-06-21 13:45:58.000000000 +0200 @@ -0,0 +1,1078 @@ +/* __builtin_object_size (ptr, object_size_type) computation + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Contributed by Jakub Jelinek + +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 */ +}; --- gcc/Makefile.in.jj 2005-06-20 12:33:35.000000000 +0200 +++ gcc/Makefile.in 2005-06-21 13:46:20.000000000 +0200 @@ -957,7 +957,7 @@ OBJS-common = \ lambda-trans.o lambda-code.o tree-loop-linear.o tree-ssa-sink.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) @@ -1946,6 +1946,9 @@ tree-loop-linear.o: tree-loop-linear.c $ 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) --- gcc/tree-ssa-ccp.c.jj 2005-06-18 01:07:29.000000000 +0200 +++ gcc/tree-ssa-ccp.c 2005-06-21 13:45:58.000000000 +0200 @@ -1965,24 +1965,49 @@ fold_stmt_r (tree *expr_p, int *walk_sub } -/* Return the string length of ARG in LENGTH. If ARG is an SSA name variable, - follow its use-def chains. If LENGTH is not NULL and its value is not - equal to the length we determine, or if we are unable to determine the - length, return false. VISITED is a bitmap of visited variables. */ +/* Return the string length, maximum string length or maximum value of + ARG in LENGTH. + If ARG is an SSA name variable, follow its use-def chains. If LENGTH + is not NULL and, for TYPE == 0, its value is not equal to the length + we determine or if we are unable to determine the length or value, + return false. VISITED is a bitmap of visited variables. + TYPE is 0 if string length should be returned, 1 for maximum string + length and 2 for maximum value ARG can have. */ static bool -get_strlen (tree arg, tree *length, bitmap visited) +get_maxval_strlen (tree arg, tree *length, bitmap visited, int type) { tree var, def_stmt, val; if (TREE_CODE (arg) != SSA_NAME) { - val = c_strlen (arg, 1); + if (type == 2) + { + val = arg; + if (TREE_CODE (val) != INTEGER_CST + || tree_int_cst_sgn (val) < 0) + return false; + } + else + val = c_strlen (arg, 1); if (!val) return false; - if (*length && simple_cst_equal (val, *length) != 1) - return false; + if (*length) + { + if (type > 0) + { + if (TREE_CODE (*length) != INTEGER_CST + || TREE_CODE (val) != INTEGER_CST) + return false; + + if (tree_int_cst_lt (*length, val)) + *length = val; + return true; + } + else if (simple_cst_equal (val, *length) != 1) + return false; + } *length = val; return true; @@ -2000,28 +2025,14 @@ get_strlen (tree arg, tree *length, bitm { case MODIFY_EXPR: { - tree len, rhs; - + tree rhs; + /* The RHS of the statement defining VAR must either have a constant length or come from another SSA_NAME with a constant length. */ rhs = TREE_OPERAND (def_stmt, 1); STRIP_NOPS (rhs); - if (TREE_CODE (rhs) == SSA_NAME) - return get_strlen (rhs, length, visited); - - /* See if the RHS is a constant length. */ - len = c_strlen (rhs, 1); - if (len) - { - if (*length && simple_cst_equal (len, *length) != 1) - return false; - - *length = len; - return true; - } - - break; + return get_maxval_strlen (rhs, length, visited, type); } case PHI_NODE: @@ -2043,7 +2054,7 @@ get_strlen (tree arg, tree *length, bitm if (arg == PHI_RESULT (def_stmt)) continue; - if (!get_strlen (arg, length, visited)) + if (!get_maxval_strlen (arg, length, visited, type)) return false; } @@ -2065,9 +2076,9 @@ get_strlen (tree arg, tree *length, bitm static tree ccp_fold_builtin (tree stmt, tree fn) { - tree result, strlen_val[2]; + tree result, val[3]; tree callee, arglist, a; - int strlen_arg, i; + int arg_mask, i, type; bitmap visited; bool ignore; @@ -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); } } --- gcc/doc/extend.texi.jj 2005-06-18 01:08:26.000000000 +0200 +++ gcc/doc/extend.texi 2005-06-21 13:45:58.000000000 +0200 @@ -71,6 +71,8 @@ extensions, accepted by GCC in C89 mode * 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. @@ -4717,6 +4719,139 @@ previous memory loads have been satisfie 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 --- gcc/builtin-attrs.def.jj 2005-05-06 10:05:56.000000000 +0200 +++ gcc/builtin-attrs.def 2005-06-21 13:45:58.000000000 +0200 @@ -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 /* 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)