From 7d384cc0b300cace24c008fec600219e4377923c Mon Sep 17 00:00:00 2001 From: Ken Raeburn Date: Sun, 11 Oct 1998 02:21:54 +0000 Subject: [PATCH] Fine-grained control of -fcheck-memory-usage with new no_check_memory_usage attribute. Fine-grained control of -fcheck-memory-usage with new no_check_memory_usage attribute. Misc minor bugfixes and tests for it too. From-SVN: r22983 --- gcc/ChangeLog | 29 ++ gcc/c-common.c | 20 +- gcc/c-decl.c | 2 + gcc/calls.c | 16 +- gcc/config/alpha/alpha.c | 4 +- gcc/config/clipper/clipper.c | 2 +- gcc/config/m88k/m88k.c | 2 +- gcc/config/pa/pa.c | 2 +- gcc/config/sparc/sparc.c | 2 +- gcc/expr.c | 49 ++-- gcc/expr.h | 4 + gcc/extend.texi | 13 + gcc/function.c | 16 +- gcc/function.h | 1 + gcc/invoke.texi | 21 +- gcc/optabs.c | 14 +- gcc/stmt.c | 8 +- gcc/testsuite/gcc.c-torture/ChangeLog | 5 + .../gcc.c-torture/execute/memcheck/blkarg.c | 64 +++++ .../gcc.c-torture/execute/memcheck/driver.c | 254 ++++++++++++++++++ .../gcc.c-torture/execute/memcheck/driver.h | 28 ++ .../execute/memcheck/memcheck.exp | 54 ++++ .../gcc.c-torture/execute/memcheck/t1.c | 27 ++ .../gcc.c-torture/execute/memcheck/t2.c | 26 ++ .../gcc.c-torture/execute/memcheck/t3.c | 25 ++ .../gcc.c-torture/execute/memcheck/t4.c | 34 +++ .../gcc.c-torture/execute/memcheck/t5.c | 33 +++ .../gcc.c-torture/execute/memcheck/t6.c | 39 +++ .../gcc.c-torture/execute/memcheck/t7.c | 40 +++ .../gcc.c-torture/execute/memcheck/t8.c | 41 +++ .../gcc.c-torture/execute/memcheck/t9.c | 40 +++ .../gcc.c-torture/execute/memcheck/template | 16 ++ gcc/tree.h | 5 + 33 files changed, 881 insertions(+), 55 deletions(-) create mode 100644 gcc/testsuite/gcc.c-torture/execute/memcheck/blkarg.c create mode 100644 gcc/testsuite/gcc.c-torture/execute/memcheck/driver.c create mode 100644 gcc/testsuite/gcc.c-torture/execute/memcheck/driver.h create mode 100644 gcc/testsuite/gcc.c-torture/execute/memcheck/memcheck.exp create mode 100644 gcc/testsuite/gcc.c-torture/execute/memcheck/t1.c create mode 100644 gcc/testsuite/gcc.c-torture/execute/memcheck/t2.c create mode 100644 gcc/testsuite/gcc.c-torture/execute/memcheck/t3.c create mode 100644 gcc/testsuite/gcc.c-torture/execute/memcheck/t4.c create mode 100644 gcc/testsuite/gcc.c-torture/execute/memcheck/t5.c create mode 100644 gcc/testsuite/gcc.c-torture/execute/memcheck/t6.c create mode 100644 gcc/testsuite/gcc.c-torture/execute/memcheck/t7.c create mode 100644 gcc/testsuite/gcc.c-torture/execute/memcheck/t8.c create mode 100644 gcc/testsuite/gcc.c-torture/execute/memcheck/t9.c create mode 100644 gcc/testsuite/gcc.c-torture/execute/memcheck/template diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 5197ef3f3ee..b1c44a00b86 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,32 @@ +Sun Oct 11 05:03:41 1998 Ken Raeburn + + * tree.h (DECL_NO_CHECK_MEMORY_USAGE): New macros. + (struct tree_decl): New fields no_check_memory_usage. + * c-common.c (enum attrs): Add A_NO_CHECK_MEMORY_USAGE. + (init_attributes): Register it as a new attribute. + (decl_attributes): Set flags on functions given that attribute. + * c-decl.c (duplicate_decls): Merge new attribute. + * expr.h (current_function_check_memory_usage): Declare new var. + * calls.c, expr.c, function.c, stmt.c, alpha.c, clipper.c, m88k.c, + pa.c, sparc.c: Replace uses of flag_check_memory_usage with + current_function_check_memory_usage. + * function.h: Add field to struct function. + * function.c (current_function_check_memory_usage): Define it. + (push_function_context_to, pop_function_context_from): Save and + restore it. + (expand_function_start): Set it, based on global flag and function + attribute. + + * expr.c (expand_expr, case VAR_DECL): In memory-checking code, do + check non-automatic variables, to permit detection of writes to + read-only locations in embedded systems without memory management. + * calls.c (store_one_arg): Use ARGS_SIZE_RTX to get size of argument + when emitting chkr_set_right_libfunc call, even if the argument is + BLKmode or variable-sized; don't abort. + + * optabs.c (init_optabs): Create Checker and __cyg_profile_* + symbols in Pmode, not VOIDmode. + Sun Oct 11 01:03:05 1998 Zack Weinberg * cppexp.c: When forcing unsigned comparisons, cast both sides diff --git a/gcc/c-common.c b/gcc/c-common.c index 820473f836c..3157e1d0f7d 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -51,7 +51,7 @@ extern struct obstack permanent_obstack; int skip_evaluation; enum attrs {A_PACKED, A_NOCOMMON, A_COMMON, A_NORETURN, A_CONST, A_T_UNION, - A_NO_INSTRUMENT_FUNCTION, + A_NO_CHECK_MEMORY_USAGE, A_NO_INSTRUMENT_FUNCTION, A_CONSTRUCTOR, A_DESTRUCTOR, A_MODE, A_SECTION, A_ALIGNED, A_UNUSED, A_FORMAT, A_FORMAT_ARG, A_WEAK, A_ALIAS, A_INIT_PRIORITY}; @@ -394,6 +394,7 @@ init_attributes () add_attribute (A_ALIAS, "alias", 1, 1, 1); add_attribute (A_INIT_PRIORITY, "init_priority", 0, 1, 0); add_attribute (A_NO_INSTRUMENT_FUNCTION, "no_instrument_function", 0, 0, 1); + add_attribute (A_NO_CHECK_MEMORY_USAGE, "no_check_memory_usage", 0, 0, 1); } /* Process the attributes listed in ATTRIBUTES and PREFIX_ATTRIBUTES @@ -889,6 +890,23 @@ decl_attributes (node, attributes, prefix_attributes) warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); break; + case A_NO_CHECK_MEMORY_USAGE: + if (TREE_CODE (decl) != FUNCTION_DECL) + { + error_with_decl (decl, + "`%s' attribute applies only to functions", + IDENTIFIER_POINTER (name)); + } + else if (DECL_INITIAL (decl)) + { + error_with_decl (decl, + "can't set `%s' attribute after definition", + IDENTIFIER_POINTER (name)); + } + else + DECL_NO_CHECK_MEMORY_USAGE (decl) = 1; + break; + case A_INIT_PRIORITY: { tree initp_expr = (args ? TREE_VALUE (args): NULL_TREE); diff --git a/gcc/c-decl.c b/gcc/c-decl.c index 99b4ad5ab52..399acb08148 100644 --- a/gcc/c-decl.c +++ b/gcc/c-decl.c @@ -1938,6 +1938,8 @@ duplicate_decls (newdecl, olddecl, different_binding_level) DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (newdecl) |= DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (olddecl); + DECL_NO_CHECK_MEMORY_USAGE (newdecl) + |= DECL_NO_CHECK_MEMORY_USAGE (olddecl); } pop_obstacks (); diff --git a/gcc/calls.c b/gcc/calls.c index 2bddc2a32a1..c44617e6bb4 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -595,7 +595,7 @@ expand_call (exp, target, ignore) if -fcheck-memory-usage, code which invokes functions (and thus damages some hard registers) can be inserted before using the value. So, target is always a pseudo-register in that case. */ - if (flag_check_memory_usage) + if (current_function_check_memory_usage) target = 0; /* See if we can find a DECL-node for the actual function. @@ -1625,7 +1625,7 @@ expand_call (exp, target, ignore) pop_temp_slots (); /* FUNEXP can't be BLKmode */ /* Check the function is executable. */ - if (flag_check_memory_usage) + if (current_function_check_memory_usage) emit_library_call (chkr_check_exec_libfunc, 1, VOIDmode, 1, funexp, ptr_mode); @@ -1864,7 +1864,7 @@ expand_call (exp, target, ignore) NULL_RTX))); /* Mark the memory for the aggregate as write-only. */ - if (flag_check_memory_usage) + if (current_function_check_memory_usage) emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, structure_value_addr, ptr_mode, @@ -3508,15 +3508,13 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl, if (arg->value == arg->stack) { - /* If the value is already in the stack slot, we are done. */ - if (flag_check_memory_usage && GET_CODE (arg->stack) == MEM) + /* If the value is already in the stack slot, we are done moving + data. */ + if (current_function_check_memory_usage && GET_CODE (arg->stack) == MEM) { - if (arg->mode == BLKmode) - abort (); - emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, XEXP (arg->stack, 0), ptr_mode, - GEN_INT (GET_MODE_SIZE (arg->mode)), + ARGS_SIZE_RTX (arg->size), TYPE_MODE (sizetype), GEN_INT (MEMORY_USE_RW), TYPE_MODE (integer_type_node)); diff --git a/gcc/config/alpha/alpha.c b/gcc/config/alpha/alpha.c index 5f738b599b2..dca15802a8f 100644 --- a/gcc/config/alpha/alpha.c +++ b/gcc/config/alpha/alpha.c @@ -2975,7 +2975,7 @@ alpha_builtin_saveregs (arglist) dest = change_address (block, ptr_mode, XEXP (block, 0)); emit_move_insn (dest, addr); - if (flag_check_memory_usage) + if (current_function_check_memory_usage) emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, dest, ptr_mode, GEN_INT (GET_MODE_SIZE (ptr_mode)), @@ -2989,7 +2989,7 @@ alpha_builtin_saveregs (arglist) POINTER_SIZE/BITS_PER_UNIT)); emit_move_insn (dest, argsize); - if (flag_check_memory_usage) + if (current_function_check_memory_usage) emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, dest, ptr_mode, GEN_INT (GET_MODE_SIZE diff --git a/gcc/config/clipper/clipper.c b/gcc/config/clipper/clipper.c index 4bee0e6aaa7..d59d3f1671e 100644 --- a/gcc/config/clipper/clipper.c +++ b/gcc/config/clipper/clipper.c @@ -438,7 +438,7 @@ clipper_builtin_saveregs (arglist) scratch); - if (flag_check_memory_usage) + if (current_function_check_memory_usage) { emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, addr, ptr_mode, diff --git a/gcc/config/m88k/m88k.c b/gcc/config/m88k/m88k.c index b1250e5e61b..877ecf9b8fa 100644 --- a/gcc/config/m88k/m88k.c +++ b/gcc/config/m88k/m88k.c @@ -2644,7 +2644,7 @@ m88k_builtin_saveregs (arglist) UNITS_PER_WORD * (8 - fixed)); } - if (flag_check_memory_usage) + if (current_function_check_memory_usage) { emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, block, ptr_mode, diff --git a/gcc/config/pa/pa.c b/gcc/config/pa/pa.c index 41642041e32..91ada8e3aec 100644 --- a/gcc/config/pa/pa.c +++ b/gcc/config/pa/pa.c @@ -4391,7 +4391,7 @@ hppa_builtin_saveregs (arglist) last argument register store. So we emit a blockage insn here. */ emit_insn (gen_blockage ()); - if (flag_check_memory_usage) + if (current_function_check_memory_usage) emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, dest, ptr_mode, GEN_INT (4 * UNITS_PER_WORD), TYPE_MODE (sizetype), diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c index ae207027b0f..686350b8694 100644 --- a/gcc/config/sparc/sparc.c +++ b/gcc/config/sparc/sparc.c @@ -4279,7 +4279,7 @@ sparc_builtin_saveregs (arglist) GEN_INT (STACK_POINTER_OFFSET + UNITS_PER_WORD * first_reg)); - if (flag_check_memory_usage + if (current_function_check_memory_usage && first_reg < NPARM_REGS (word_mode)) emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, address, ptr_mode, diff --git a/gcc/expr.c b/gcc/expr.c index d2dcdf8c592..53dce8fdb0e 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -110,8 +110,8 @@ static rtx apply_args_value; static int can_handle_constant_p; /* Don't check memory usage, since code is being emitted to check a memory - usage. Used when flag_check_memory_usage is true, to avoid infinite - recursion. */ + usage. Used when current_function_check_memory_usage is true, to avoid + infinite recursion. */ static int in_check_memory_usage; /* This structure is used by move_by_pieces to describe the move to @@ -2865,7 +2865,7 @@ emit_push_insn (x, mode, type, size, align, partial, reg, extra, move_by_pieces (gen_rtx_MEM (BLKmode, gen_push_operand ()), xinner, INTVAL (size) - used, align); - if (flag_check_memory_usage && ! in_check_memory_usage) + if (current_function_check_memory_usage && ! in_check_memory_usage) { rtx temp; @@ -2922,7 +2922,7 @@ emit_push_insn (x, mode, type, size, align, partial, reg, extra, args_addr, args_so_far), skip)); - if (flag_check_memory_usage && ! in_check_memory_usage) + if (current_function_check_memory_usage && ! in_check_memory_usage) { rtx target; @@ -3122,7 +3122,7 @@ emit_push_insn (x, mode, type, size, align, partial, reg, extra, emit_move_insn (gen_rtx_MEM (mode, addr), x); - if (flag_check_memory_usage && ! in_check_memory_usage) + if (current_function_check_memory_usage && ! in_check_memory_usage) { in_check_memory_usage = 1; if (target == 0) @@ -3290,7 +3290,7 @@ expand_assignment (to, from, want_value, suggest_reg) } /* Check the access. */ - if (flag_check_memory_usage && GET_CODE (to_rtx) == MEM) + if (current_function_check_memory_usage && GET_CODE (to_rtx) == MEM) { rtx to_addr; int size; @@ -3416,7 +3416,7 @@ expand_assignment (to, from, want_value, suggest_reg) EXPAND_MEMORY_USE_DONT); /* Copy the rights of the bitmap. */ - if (flag_check_memory_usage) + if (current_function_check_memory_usage) emit_library_call (chkr_copy_bitmap_libfunc, 1, VOIDmode, 3, XEXP (to_rtx, 0), ptr_mode, XEXP (from_rtx, 0), ptr_mode, @@ -3638,7 +3638,7 @@ store_expr (exp, target, want_value) temp = convert_modes (GET_MODE (target), TYPE_MODE (TREE_TYPE (exp)), temp, TREE_UNSIGNED (TREE_TYPE (exp))); - if (flag_check_memory_usage + if (current_function_check_memory_usage && GET_CODE (target) == MEM && AGGREGATE_TYPE_P (TREE_TYPE (exp))) { @@ -3742,7 +3742,7 @@ store_expr (exp, target, want_value) if (size != const0_rtx) { /* Be sure we can write on ADDR. */ - if (flag_check_memory_usage) + if (current_function_check_memory_usage) emit_library_call (chkr_check_addr_libfunc, 1, VOIDmode, 3, addr, ptr_mode, size, TYPE_MODE (sizetype), @@ -5584,13 +5584,16 @@ expand_expr (exp, target, tmode, modifier) pop_obstacks (); } - /* Only check automatic variables. Currently, function arguments are - not checked (this can be done at compile-time with prototypes). - Aggregates are not checked. */ - if (flag_check_memory_usage && code == VAR_DECL + /* Although static-storage variables start off initialized, according to + ANSI C, a memcpy could overwrite them with uninitialized values. So + we check them too. This also lets us check for read-only variables + accessed via a non-const declaration, in case it won't be detected + any other way (e.g., in an embedded system or OS kernel without + memory protection). + + Aggregates are not checked here; they're handled elsewhere. */ + if (current_function_check_memory_usage && code == VAR_DECL && GET_CODE (DECL_RTL (exp)) == MEM - && DECL_CONTEXT (exp) != NULL_TREE - && ! TREE_STATIC (exp) && ! AGGREGATE_TYPE_P (TREE_TYPE (exp))) { enum memory_use_mode memory_usage; @@ -6107,7 +6110,7 @@ expand_expr (exp, target, tmode, modifier) op0 = expand_expr (exp1, NULL_RTX, VOIDmode, EXPAND_SUM); op0 = memory_address (mode, op0); - if (flag_check_memory_usage && !AGGREGATE_TYPE_P (TREE_TYPE (exp))) + if (current_function_check_memory_usage && !AGGREGATE_TYPE_P (TREE_TYPE (exp))) { enum memory_use_mode memory_usage; memory_usage = get_memory_usage_from_modifier (modifier); @@ -6411,7 +6414,7 @@ expand_expr (exp, target, tmode, modifier) } /* Check the access. */ - if (flag_check_memory_usage && GET_CODE (op0) == MEM) + if (current_function_check_memory_usage && GET_CODE (op0) == MEM) { enum memory_use_mode memory_usage; memory_usage = get_memory_usage_from_modifier (modifier); @@ -9163,7 +9166,7 @@ expand_builtin (exp, target, subtarget, mode, ignore) src_rtx = copy_to_mode_reg (Pmode, src_rtx); /* Check the string is readable and has an end. */ - if (flag_check_memory_usage) + if (current_function_check_memory_usage) emit_library_call (chkr_check_str_libfunc, 1, VOIDmode, 2, src_rtx, ptr_mode, GEN_INT (MEMORY_USE_RO), @@ -9256,7 +9259,7 @@ expand_builtin (exp, target, subtarget, mode, ignore) len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); /* Just copy the rights of SRC to the rights of DEST. */ - if (flag_check_memory_usage) + if (current_function_check_memory_usage) emit_library_call (chkr_copy_bitmap_libfunc, 1, VOIDmode, 3, XEXP (dest_mem, 0), ptr_mode, XEXP (src_mem, 0), ptr_mode, @@ -9327,7 +9330,7 @@ expand_builtin (exp, target, subtarget, mode, ignore) dest_mem = get_memory_rtx (dest); /* Just check DST is writable and mark it as readable. */ - if (flag_check_memory_usage) + if (current_function_check_memory_usage) emit_library_call (chkr_check_addr_libfunc, 1, VOIDmode, 3, XEXP (dest_mem, 0), ptr_mode, len_rtx, TYPE_MODE (sizetype), @@ -9353,7 +9356,7 @@ expand_builtin (exp, target, subtarget, mode, ignore) break; /* If we need to check memory accesses, call the library function. */ - if (flag_check_memory_usage) + if (current_function_check_memory_usage) break; if (arglist == 0 @@ -9409,7 +9412,7 @@ expand_builtin (exp, target, subtarget, mode, ignore) break; /* If we need to check memory accesses, call the library function. */ - if (flag_check_memory_usage) + if (current_function_check_memory_usage) break; if (arglist == 0 @@ -10238,7 +10241,7 @@ expand_increment (exp, post, ignore) /* Increment however we can. */ op1 = expand_binop (mode, this_optab, value, op1, - flag_check_memory_usage ? NULL_RTX : op0, + current_function_check_memory_usage ? NULL_RTX : op0, TREE_UNSIGNED (TREE_TYPE (exp)), OPTAB_LIB_WIDEN); /* Make sure the value is stored into OP0. */ if (op1 != op0) diff --git a/gcc/expr.h b/gcc/expr.h index 353e6fdfe46..b1665aa7de9 100644 --- a/gcc/expr.h +++ b/gcc/expr.h @@ -91,6 +91,10 @@ extern int current_function_uses_pic_offset_table; /* The arg pointer hard register, or the pseudo into which it was copied. */ extern rtx current_function_internal_arg_pointer; +/* This is nonzero if memory access checking be enabled in the current + function. */ +extern int current_function_check_memory_usage; + /* Nonzero means stack pops must not be deferred, and deferred stack pops must not be output. It is nonzero inside a function call, inside a conditional expression, inside a statement expression, diff --git a/gcc/extend.texi b/gcc/extend.texi index 45b31b82322..a2c278b001c 100644 --- a/gcc/extend.texi +++ b/gcc/extend.texi @@ -1518,6 +1518,19 @@ mangled name for the target must be used. Not all target machines support this attribute. +@item no_check_memory_usage +@cindex @code{no_check_memory_usage} function attribute +If @samp{-fcheck-memory-usage} is given, calls to support routines will +be generated before most memory accesses, to permit support code to +record usage and detect uses of uninitialized or unallocated storage. +Since the compiler cannot handle them properly, @code{asm} statements +are not allowed. Declaring a function with this attribute disables the +memory checking code for that function, permitting the use of @code{asm} +statements without requiring separate compilation with different +options, and allowing you to write support routines of your own if you +wish, without getting infinite recursion if they get compiled with this +option. + @item regparm (@var{number}) @cindex functions that are passed arguments in registers on the 386 On the Intel 386, the @code{regparm} attribute causes the compiler to diff --git a/gcc/function.c b/gcc/function.c index 5ad113cfc93..4645f5af7cc 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -221,6 +221,9 @@ char *current_function_cannot_inline; generated. */ int current_function_instrument_entry_exit; +/* Nonzero if memory access checking be enabled in the current function. */ +int current_function_check_memory_usage; + /* The FUNCTION_DECL for an inline function currently being expanded. */ tree inline_function_decl; @@ -543,6 +546,7 @@ push_function_context_to (context) p->fixup_var_refs_queue = 0; p->epilogue_delay_list = current_function_epilogue_delay_list; p->args_info = current_function_args_info; + p->check_memory_usage = current_function_check_memory_usage; p->instrument_entry_exit = current_function_instrument_entry_exit; save_tree_status (p, context); @@ -626,6 +630,7 @@ pop_function_context_from (context) current_function_epilogue_delay_list = p->epilogue_delay_list; reg_renumber = 0; current_function_args_info = p->args_info; + current_function_check_memory_usage = p->check_memory_usage; current_function_instrument_entry_exit = p->instrument_entry_exit; restore_tree_status (p, context); @@ -1484,7 +1489,7 @@ put_var_into_stack (decl) else return; - if (flag_check_memory_usage) + if (current_function_check_memory_usage) emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, XEXP (reg, 0), ptr_mode, GEN_INT (GET_MODE_SIZE (GET_MODE (reg))), @@ -4364,7 +4369,7 @@ assign_parms (fndecl, second_time) store_expr (parm, copy, 0); emit_move_insn (parmreg, XEXP (copy, 0)); - if (flag_check_memory_usage) + if (current_function_check_memory_usage) emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, XEXP (copy, 0), ptr_mode, GEN_INT (int_size_in_bytes (type)), @@ -4529,7 +4534,7 @@ assign_parms (fndecl, second_time) emit_move_insn (validize_mem (stack_parm), validize_mem (entry_parm)); } - if (flag_check_memory_usage) + if (current_function_check_memory_usage) { push_to_sequence (conversion_insns); emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, @@ -5550,6 +5555,11 @@ expand_function_start (subr, parms_have_cleanups) valid operands of arithmetic insns. */ init_recog_no_volatile (); + /* Set this before generating any memory accesses. */ + current_function_check_memory_usage + = (flag_check_memory_usage + && ! DECL_NO_CHECK_MEMORY_USAGE (current_function_decl)); + current_function_instrument_entry_exit = (flag_instrument_function_entry_exit && ! DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (subr)); diff --git a/gcc/function.h b/gcc/function.h index 06e90dc88aa..014ed135b74 100644 --- a/gcc/function.h +++ b/gcc/function.h @@ -151,6 +151,7 @@ struct function rtx saveregs_value; rtx apply_args_value; rtx forced_labels; + int check_memory_usage; /* For emit-rtl.c. */ int reg_rtx_no; diff --git a/gcc/invoke.texi b/gcc/invoke.texi index d0339e47699..cf461987208 100644 --- a/gcc/invoke.texi +++ b/gcc/invoke.texi @@ -5843,8 +5843,7 @@ the offsets of structure members won't agree with system libraries. @item -fcheck-memory-usage Generate extra code to check each memory access. GNU CC will generate code that is suitable for a detector of bad memory accesses such as -@file{Checker}. If you specify this option, you can not use the -@code{asm} or @code{__asm__} keywords. +@file{Checker}. You must also specify this option when you compile functions you call that have side effects. If you do not, you may get erroneous messages from @@ -5859,6 +5858,24 @@ which are provided by the detector. If you cannot find or build stubs for every function you call, you may have to specify @samp{-fcheck-memory-usage} without @samp{-fprefix-function-name}. +If you specify this option, you can not use the @code{asm} or +@code{__asm__} keywords in functions with memory checking enabled. The +compiler cannot understand what the @code{asm} statement will do, and +therefore cannot generate the appropriate code, so it is rejected. +However, the function attribute @code{no_check_memory_usage} will +disable memory checking within a function, and @code{asm} statements can +be put inside such functions. Inline expansion of a non-checked +function within a checked function is permitted; the inline function's +memory accesses won't be checked, but the rest will. + +If you move your @code{asm} statements to non-checked inline functions, +but they do access memory, you can add calls to the support code in your +inline function, to indicate any reads, writes, or copies being done. +These calls would be similar to those done in the stubs described above. + +@c FIXME: The support-routine interface is defined by the compiler and +@c should be documented! + @item -fprefix-function-name Request GNU CC to add a prefix to the symbols generated for function names. GNU CC adds a prefix to the names of functions defined as well as diff --git a/gcc/optabs.c b/gcc/optabs.c index 2157f3f8784..2a321690dc4 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -4390,17 +4390,17 @@ init_optabs () fixunstfti_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunstfti"); /* For check-memory-usage. */ - chkr_check_addr_libfunc = gen_rtx_SYMBOL_REF (VOIDmode, "chkr_check_addr"); - chkr_set_right_libfunc = gen_rtx_SYMBOL_REF (VOIDmode, "chkr_set_right"); - chkr_copy_bitmap_libfunc = gen_rtx_SYMBOL_REF (VOIDmode, "chkr_copy_bitmap"); - chkr_check_exec_libfunc = gen_rtx_SYMBOL_REF (VOIDmode, "chkr_check_exec"); - chkr_check_str_libfunc = gen_rtx_SYMBOL_REF (VOIDmode, "chkr_check_str"); + chkr_check_addr_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_check_addr"); + chkr_set_right_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_set_right"); + chkr_copy_bitmap_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_copy_bitmap"); + chkr_check_exec_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_check_exec"); + chkr_check_str_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_check_str"); /* For function entry/exit instrumentation. */ profile_function_entry_libfunc - = gen_rtx_SYMBOL_REF (VOIDmode, "__cyg_profile_func_enter"); + = gen_rtx_SYMBOL_REF (Pmode, "__cyg_profile_func_enter"); profile_function_exit_libfunc - = gen_rtx_SYMBOL_REF (VOIDmode, "__cyg_profile_func_exit"); + = gen_rtx_SYMBOL_REF (Pmode, "__cyg_profile_func_exit"); #ifdef HAVE_conditional_trap init_traps (); diff --git a/gcc/stmt.c b/gcc/stmt.c index 658c872fad4..b28df90c48f 100644 --- a/gcc/stmt.c +++ b/gcc/stmt.c @@ -584,7 +584,7 @@ expand_computed_goto (exp) emit_queue (); /* Be sure the function is executable. */ - if (flag_check_memory_usage) + if (current_function_check_memory_usage) emit_library_call (chkr_check_exec_libfunc, 1, VOIDmode, 1, x, ptr_mode); @@ -1118,7 +1118,7 @@ void expand_asm (body) tree body; { - if (flag_check_memory_usage) + if (current_function_check_memory_usage) { error ("`asm' cannot be used with `-fcheck-memory-usage'"); return; @@ -1174,7 +1174,7 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line) if (noutputs == 0) vol = 1; - if (flag_check_memory_usage) + if (current_function_check_memory_usage) { error ("`asm' cannot be used with `-fcheck-memory-usage'"); return; @@ -3291,7 +3291,7 @@ expand_decl (decl) && ! TREE_ADDRESSABLE (decl) && (DECL_REGISTER (decl) || ! obey_regdecls) /* if -fcheck-memory-usage, check all variables. */ - && ! flag_check_memory_usage) + && ! current_function_check_memory_usage) { /* Automatic variable that can go in a register. */ int unsignedp = TREE_UNSIGNED (type); diff --git a/gcc/testsuite/gcc.c-torture/ChangeLog b/gcc/testsuite/gcc.c-torture/ChangeLog index 9b2d6badaa5..14cd5746146 100644 --- a/gcc/testsuite/gcc.c-torture/ChangeLog +++ b/gcc/testsuite/gcc.c-torture/ChangeLog @@ -1,3 +1,8 @@ +Sun Oct 11 05:04:28 1998 Ken Raeburn + + * execute/memcheck: New directory of tests for + -fcheck-memory-usage. + 1998-10-06 Ken Raeburn * special/981006-1.c: New test. Make sure gcc doesn't lose track diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/blkarg.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/blkarg.c new file mode 100644 index 00000000000..43c5b39498a --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/blkarg.c @@ -0,0 +1,64 @@ +/* Must define: + int expect_error; + void test (); + void setup () NOCHECK; */ + +#include "driver.h" + +/* Test permissions of BLKmode arguments constructed purely on the + stack. + + Maybe we can't guarantee that we'll always wind up with stack args, + but if we don't, they're in registers, and permissions should just + always yield success. So while this test may not be effective on + all platforms, failure probably does indicate a real bug. + + Note that because of the implementation, we do want to test BLKmode + arguments that live purely on the stack and are constructed there. + We want to test other situations of function arguments, of course, + but don't assume this case would be covered by using one monster + argument that is read from memory (including using constructor + syntax but constant values), or may live partially in registers. */ + +int expect_error = 0; + +/* Must be BLKmode. Using only two fields gets TImode on Alpha. */ +struct S { + unsigned long long ll; + long xx, yy; +}; + +unsigned long long x = 0x12345689ULL; +#define I2 42 + +/* Leading six arguments force X into stack on both Alpha and MIPS. */ + +static int first_time = 1; +int foo (int a1, int a2, int a3, int a4, int a5, int a6, struct S s) { + if (a1 != 1 || a2 != 2 || a3 != 3 || a4 != 4 || a5 != 5 || a6 != 6) + abort (); + if (first_time) + { + if (s.ll != x || s.xx != I2 || s.yy != 0) + abort (); + first_time = 0; + } + else + { + if (s.ll != 0 || s.xx != 0 || s.yy != 0) + abort (); + } + return 0; +} + +void test () +{ + foo (1, 2, 3, 4, 5, 6, (struct S) { x, I2 }); + foo (1, 2, 3, 4, 5, 6, (struct S) { 0 }); +} + +void setup () /* NOCHECK */ +{ + mark_region (&x, sizeof (x), ACCESS_RO); + mark_region (&first_time, sizeof (first_time), ACCESS_RW); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/driver.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/driver.c new file mode 100644 index 00000000000..9002a0b8d24 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/driver.c @@ -0,0 +1,254 @@ +/* GNU C dependencies: + Checker support hooks + ISO C 9x array element initialization + void-pointer arithmetic */ + +#include "driver.h" + +int verbose = 0; +int debug = 0; +int bad_accesses = 0; + +const char *const memory_use_strings[] = { +#define INIT(x) [x] = #x + INIT (MEMORY_USE_BAD), + INIT (MEMORY_USE_DONT), + INIT (MEMORY_USE_RO), + INIT (MEMORY_USE_RW), + INIT (MEMORY_USE_TW), + INIT (MEMORY_USE_WO), +#undef INIT +}; + +/* This won't be used for any really huge test cases, so a simple + linked list is adequate. We won't even worry about overlapping + regions; the matching entry that comes up first wins. */ +const char *const access_mode_strings[] = { + "none", "ro", "wo", "rw", +}; +struct access_node { + struct access_node *next; + const void *addr; + size_t sz; + enum access_mode mode; +}; + +static struct access_node *access_list; + +void mark_region (const void *addr, size_t sz, enum access_mode mode) +{ + struct access_node *a; + if (debug) + printf ("mark_region (%p, %ld, %s)\n", addr, (long) sz, + access_mode_strings[mode]); + a = malloc (sizeof (struct access_node)); + a->next = access_list; + a->addr = addr; + a->sz = sz; + a->mode = mode; + access_list = a; +} + +void report_bad_access (void *, size_t, enum memory_use_mode) NOCHECK; +void report_bad_access (void *addr, size_t sz, enum memory_use_mode mode) +{ + if (++bad_accesses > 100) + bad_accesses = 100; + if (verbose) + { + static char x[100]; + const char *mode_str; + if (mode >= 0 + && mode < sizeof (memory_use_strings) / sizeof (*memory_use_strings) + && memory_use_strings[mode] != 0) + mode_str = memory_use_strings[mode]; + else + { + sprintf (x, "", mode); + mode_str = x; + } + printf ("bad access (%p, %ld, %s)\n", addr, (long) sz, mode_str); + } +} + +int verify1 (void *, size_t, enum access_mode, struct access_node *) NOCHECK; +int verify1 (void *addr, size_t sz, enum access_mode mode, + struct access_node *a) +{ + while (a && (addr + sz <= a->addr || addr >= a->addr + a->sz)) + a = a->next; + if (a == 0) + return 0; + + if (debug) + printf ("verify1 (%p, %ld, %s)\n", addr, (long) sz, + access_mode_strings[mode]); + + if (mode & ~a->mode) + return 0; + + if (addr < a->addr) + if (verify1 (a, a->addr - addr, mode, a->next) == 0) + return 0; + if (addr + sz > a->addr + a->sz) + if (verify1 (a->addr + a->sz, (addr + sz) - (a->addr + a->sz), mode, a->next) == 0) + return 0; + + /* All regions okay. */ + return 1; +} + +int verify_range_permission (void *, size_t, enum access_mode) NOCHECK; + +int verify_range_permission (void *addr, size_t sz, enum access_mode mode) +{ + if (debug) + printf ("verify_range_permission (%p, %ld, %s)\n", addr, (long) sz, + access_mode_strings[mode]); + return verify1 (addr, sz, mode, access_list); +} + +void chkr_check_addr (void *, size_t, int) NOCHECK; + +void chkr_check_addr (void *addr, size_t sz, int mode) +{ + switch (mode) + { + case MEMORY_USE_BAD: + case MEMORY_USE_DONT: + default: + report_bad_access (addr, sz, mode); + return; + case MEMORY_USE_RO: + /* verify range readable */ + if (!verify_range_permission (addr, sz, ACCESS_RO)) + report_bad_access (addr, sz, mode); + return; + case MEMORY_USE_WO: + /* verify writeable, set writeable and readable */ + if (!verify_range_permission (addr, sz, ACCESS_WO)) + report_bad_access (addr, sz, mode); + mark_region (addr, sz, ACCESS_RW); + return; + case MEMORY_USE_RW: + /* verify readable and writeable, no change */ + if (!verify_range_permission (addr, sz, ACCESS_RW)) + report_bad_access (addr, sz, mode); + return; + case MEMORY_USE_TW: + /* verify writeable, no change */ + if (!verify_range_permission (addr, sz, ACCESS_WO)) + report_bad_access (addr, sz, mode); + return; + } + /* All branches should return. */ + abort (); +} + +void copy1 (void *, void *, size_t, struct access_node *) NOCHECK; +void copy1 (void *dest, void *src, size_t sz, struct access_node *a) +{ + while (a && (src + sz <= a->addr || src >= a->addr + a->sz)) + a = a->next; + if (a == 0) + { + report_bad_access (src, sz, MEMORY_USE_RO); + return; + } + + if (debug) + printf ("copy1 (%p, %p, %ld)\n", dest, src, (long) sz); + + { + void *start, *end; + start = src; + if (start < a->addr) + start = a->addr; + end = src + sz; + if (end > a->addr + a->sz) + end = a->addr + a->sz; + mark_region (dest + (start - src), end - start, a->mode); + } + + if (src < a->addr) + copy1 (dest, src, a->addr - src, a->next); + if (src + sz > a->addr + a->sz) + copy1 (dest + (a->addr + a->sz - src), a->addr + a->sz, + (src + sz) - (a->addr + a->sz), a->next); +} + +void chkr_copy_bitmap (void *, void *, size_t) NOCHECK; +void chkr_copy_bitmap (void *dest, void *src, size_t sz) +{ + if (verify_range_permission (dest, sz, MEMORY_USE_WO) == 0) + report_bad_access (dest, sz, MEMORY_USE_WO); + copy1 (dest, src, sz, access_list); +} + +void chkr_set_right (void *, size_t, enum access_mode) NOCHECK; +void chkr_set_right (void *addr, size_t sz, enum access_mode mode) +{ + mark_region (addr, sz, mode); +} + +int main () NOCHECK; +int main () +{ + setup (); + test (); + bad_accesses = !!bad_accesses; /* get 0 or 1 */ + /* Return 0 if got expected results, 1 otherwise. */ + return !(bad_accesses == expect_error); +} + +struct malloc_node { + struct malloc_node *next; + void *addr; + size_t sz; + unsigned is_free : 1; +}; +static struct malloc_node *malloc_list; + +void *c_malloc (size_t sz) +{ + void *p; + struct malloc_node *m; + if (sz == 0) + return 0; + p = malloc (sz); + if (p == 0) + { + if (verbose) + printf ("malloc(%ld) failed\n", (long) sz); + exit (1); + } + m = malloc (sizeof (struct malloc_node)); + if (m == 0) + { + if (verbose) + printf ("malloc(%ld) failed\n", (long) sizeof (struct malloc_node)); + exit (1); + } + mark_region (p, sz, ACCESS_WO); + m->addr = p; + m->sz = sz; + m->is_free = 0; + m->next = malloc_list; + malloc_list = m; + return p; +} + +void c_free (void *p) +{ + struct malloc_node *m; + if (p == 0) + return; + for (m = malloc_list; m; m = m->next) + if (m->addr == p) + break; + if (m == 0 || m->is_free) + /* Test is broken. */ + abort (); + m->is_free = 1; + free (p); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/driver.h b/gcc/testsuite/gcc.c-torture/execute/memcheck/driver.h new file mode 100644 index 00000000000..d8d22d2496a --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/driver.h @@ -0,0 +1,28 @@ +/* GNU C dependencies: + Checker support hooks + ISO C 9x array element initialization + void-pointer arithmetic */ + +typedef __SIZE_TYPE__ size_t; + +extern void *malloc (size_t); +extern int printf (const char *, ...); + +/* This comes from gcc internals. Should be exported. */ +enum memory_use_mode {MEMORY_USE_BAD = 0, MEMORY_USE_RO = 1, + MEMORY_USE_WO = 2, MEMORY_USE_RW = 3, + MEMORY_USE_TW = 6, MEMORY_USE_DONT = 99}; + +enum access_mode { + ACCESS_NONE = 0, ACCESS_RO = 1, ACCESS_WO = 2, ACCESS_RW = 3 +}; + +#define NOCHECK __attribute__ ((no_check_memory_usage)) + +void mark_region (const void *, size_t, enum access_mode) NOCHECK; +void setup () NOCHECK; +void test (); +extern int expect_error; + +void *c_malloc (size_t) NOCHECK; +void c_free (void *) NOCHECK; diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/memcheck.exp b/gcc/testsuite/gcc.c-torture/execute/memcheck/memcheck.exp new file mode 100644 index 00000000000..7fb756b4f5c --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/memcheck.exp @@ -0,0 +1,54 @@ +# Copyright (C) 1991, 92-93, 95, 97, 1998 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +# Please email any bugs, comments, and/or additions to this file to: +# bug-gcc@prep.ai.mit.edu + +# This file was written by Rob Savoye. (rob@cygnus.com) +# Modified and maintained by Jeffrey Wheat (cassidy@cygnus.com) + +# +# These tests come from Torbjorn Granlund (tege@cygnus.com) +# C torture test suite. +# + +if $tracelevel then { + strace $tracelevel +} + +# load support procs +load_lib c-torture.exp + +# +# main test loop +# + +set tests [lsort [glob -nocomplain $srcdir/$subdir/*.c]] +set idx [lsearch $tests */driver.c] +if $idx>=0 { + set tests [lreplace $tests $idx $idx] +} else { + error "list can't find driver.c in $srcdir/$subdir" +} +gcc_target_compile $srcdir/$subdir/driver.c driver.o object {additional_flags=-w additional_flags=-g} +foreach src $tests { + # If we're only testing specific files and this isn't one of them, skip it. + if ![runtest_file_p $runtests $src] then { + continue + } + + c-torture-execute $src "-fcheck-memory-usage driver.o" +} diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/t1.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/t1.c new file mode 100644 index 00000000000..03b6acc1335 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/t1.c @@ -0,0 +1,27 @@ +/* Must define: + int expect_error; + void test (); + void setup (); -- NOCHECK */ + +#include "driver.h" + +int expect_error = 0; + +int *ip; + +void test () +{ + ip = c_malloc (sizeof (int)); + *ip = 42; + t2 (); +} + +int t2 () +{ + return *ip; +} + +void setup () /* NOCHECK */ +{ + mark_region (&ip, sizeof (ip), ACCESS_RW); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/t2.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/t2.c new file mode 100644 index 00000000000..d386eb7ebb1 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/t2.c @@ -0,0 +1,26 @@ +/* Must define: + int expect_error; + void test (); + void setup () NOCHECK; */ + +#include "driver.h" + +int expect_error = 1; + +int *ip; + +void test () +{ + ip = c_malloc (sizeof (int)); + t2 (); +} + +int t2 () +{ + return *ip; +} + +void setup () /* NOCHECK */ +{ + mark_region (&ip, sizeof (ip), ACCESS_RW); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/t3.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/t3.c new file mode 100644 index 00000000000..5b6333d4e8e --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/t3.c @@ -0,0 +1,25 @@ +/* Must define: + int expect_error; + void test (); + void setup () NOCHECK; */ + +#include "driver.h" + +int expect_error = 0; + +int *ip; + +void test () +{ + ip = c_malloc (sizeof (int)); + t2 (ip); +} + +int t2 (int *ip) +{ +} + +void setup () /* NOCHECK */ +{ + mark_region (&ip, sizeof (ip), ACCESS_RW); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/t4.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/t4.c new file mode 100644 index 00000000000..25010a06818 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/t4.c @@ -0,0 +1,34 @@ +/* Must define: + int expect_error; + void test (); + void setup () NOCHECK; */ + +#include "driver.h" + +int expect_error = 0; + +struct s { + char c; + int a, b; +}; + +struct s *sp; + +void test () +{ + sp = c_malloc (sizeof (struct s)); + sp->c = 0; + sp->a = 12; + sp->b = 47; + foo (sp); +} + +int foo (struct s *sp) +{ + return sp->c + sp->a + sp->b; +} + +void setup () /* NOCHECK */ +{ + mark_region (&sp, sizeof (sp), ACCESS_RW); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/t5.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/t5.c new file mode 100644 index 00000000000..c3bbf640517 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/t5.c @@ -0,0 +1,33 @@ +/* Must define: + int expect_error; + void test (); + void setup () NOCHECK; */ + +#include "driver.h" + +int expect_error = 1; + +struct s { + char c; + int a, b; +}; + +struct s *sp; + +void test () +{ + sp = c_malloc (sizeof (struct s)); + sp->c = 0; + sp->b = 47; + foo (sp); +} + +int foo (struct s *sp) +{ + return sp->c + sp->a + sp->b; +} + +void setup () /* NOCHECK */ +{ + mark_region (&sp, sizeof (sp), ACCESS_RW); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/t6.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/t6.c new file mode 100644 index 00000000000..652d33d0f8c --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/t6.c @@ -0,0 +1,39 @@ +/* Must define: + int expect_error; + void test (); + void setup () NOCHECK; */ + +#include "driver.h" + +int expect_error = 1; + +struct s { + char c; + int a, b; +}; + +struct s *sp; + +void test () +{ + sp = c_malloc (sizeof (struct s) * 2); + sp->c = 0; + sp->b = 47; + cp (sp); + foo (sp); +} + +int foo (struct s *sp) +{ + return sp[1].c + sp[1].a + sp[1].b; +} + +int cp (struct s *sp) +{ + sp[1] = sp[0]; +} + +void setup () /* NOCHECK */ +{ + mark_region (&sp, sizeof (sp), ACCESS_RW); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/t7.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/t7.c new file mode 100644 index 00000000000..a7c6f5127ed --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/t7.c @@ -0,0 +1,40 @@ +/* Must define: + int expect_error; + void test (); + void setup () NOCHECK; */ + +#include "driver.h" + +int expect_error = 0; + +struct s { + char c; + int a, b; +}; + +struct s *sp; + +void test () +{ + sp = c_malloc (sizeof (struct s) * 2); + sp->c = 0; + sp->a = 13; + sp->b = 47; + cp (sp); + foo (sp); +} + +int foo (struct s *sp) +{ + return sp[1].c + sp[1].a + sp[1].b; +} + +int cp (struct s *sp) +{ + sp[1] = sp[0]; +} + +void setup () /* NOCHECK */ +{ + mark_region (&sp, sizeof (sp), ACCESS_RW); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/t8.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/t8.c new file mode 100644 index 00000000000..01c167247f8 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/t8.c @@ -0,0 +1,41 @@ +/* Must define: + int expect_error; + void test (); + void setup () NOCHECK; */ + +#include "driver.h" + +int expect_error = 0; + +typedef struct { + short a; + char b; +} S1; +typedef struct { + struct { int x; S1 *s1p; } *p; +} S2; + +S1 *s1; +S2 *s2; + +void test () +{ + s1 = c_malloc (sizeof (S1)); + s2 = c_malloc (sizeof (S2)); + s2->p = c_malloc (sizeof (*s2->p)); + s2->p->s1p = s1; + s1->a = 47; + s1->b = 3; + foo (); +} + +int foo () +{ + return s2->p->s1p->b; +} + +void setup () /* NOCHECK */ +{ + mark_region (&s1, sizeof (s1), ACCESS_RW); + mark_region (&s2, sizeof (s2), ACCESS_RW); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/t9.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/t9.c new file mode 100644 index 00000000000..f32ca011dd8 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/t9.c @@ -0,0 +1,40 @@ +/* Must define: + int expect_error; + void test (); + void setup () NOCHECK; */ + +#include "driver.h" + +int expect_error = 1; + +typedef struct { + short a; + char b; +} S1; +typedef struct { + struct { int x; S1 *s1p; } *p; +} S2; + +S1 *s1; +S2 *s2; + +void test () +{ + s1 = c_malloc (sizeof (S1)); + s2 = c_malloc (sizeof (S2)); + s2->p = c_malloc (sizeof (*s2->p)); + s2->p->s1p = s1; + s1->a = 47; + foo (); +} + +int foo () +{ + return s2->p->s1p->b; +} + +void setup () /* NOCHECK */ +{ + mark_region (&s1, sizeof (s1), ACCESS_RW); + mark_region (&s2, sizeof (s2), ACCESS_RW); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/template b/gcc/testsuite/gcc.c-torture/execute/memcheck/template new file mode 100644 index 00000000000..37ebb137c87 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/template @@ -0,0 +1,16 @@ +/* Must define: + int expect_error; + void test (); + void setup () NOCHECK; */ + +#include "driver.h" + +int expect_error = ; + +void test () +{ +} + +void setup () /* NOCHECK */ +{ +} diff --git a/gcc/tree.h b/gcc/tree.h index cd9fdbb163e..8bd8df3368f 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -1226,6 +1226,10 @@ struct tree_type be instrumented with calls to support routines. */ #define DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(NODE) ((NODE)->decl.no_instrument_function_entry_exit) +/* Used in FUNCTION_DECLs to indicate that in this function, + check-memory-usage should be disabled. */ +#define DECL_NO_CHECK_MEMORY_USAGE(NODE) ((NODE)->decl.no_check_memory_usage) + /* Additional flags for language-specific uses. */ #define DECL_LANG_FLAG_0(NODE) (DECL_CHECK (NODE)->decl.lang_flag_0) #define DECL_LANG_FLAG_1(NODE) (DECL_CHECK (NODE)->decl.lang_flag_1) @@ -1282,6 +1286,7 @@ struct tree_decl unsigned non_addr_const_p : 1; unsigned no_instrument_function_entry_exit : 1; + unsigned no_check_memory_usage : 1; /* For a FUNCTION_DECL, if inline, this is the size of frame needed. If built-in, this is the code for which built-in function.