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
This commit is contained in:
Ken Raeburn 1998-10-11 02:21:54 +00:00 committed by Ken Raeburn
parent e41887f1fc
commit 7d384cc0b3
33 changed files with 881 additions and 55 deletions

View File

@ -1,3 +1,32 @@
Sun Oct 11 05:03:41 1998 Ken Raeburn <raeburn@cygnus.com>
* 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 <zack@rabi.phys.columbia.edu>
* cppexp.c: When forcing unsigned comparisons, cast both sides

View File

@ -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);

View File

@ -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 ();

View File

@ -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));

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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),

View File

@ -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,

View File

@ -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)

View File

@ -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,

View File

@ -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

View File

@ -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));

View File

@ -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;

View File

@ -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

View File

@ -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 ();

View File

@ -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);

View File

@ -1,3 +1,8 @@
Sun Oct 11 05:04:28 1998 Ken Raeburn <raeburn@cygnus.com>
* execute/memcheck: New directory of tests for
-fcheck-memory-usage.
1998-10-06 Ken Raeburn <raeburn@cygnus.com>
* special/981006-1.c: New test. Make sure gcc doesn't lose track

View File

@ -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);
}

View File

@ -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, "<bad mode %d>", 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);
}

View File

@ -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;

View File

@ -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"
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 */
{
}

View File

@ -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.