PR middle-end/49905 - Better sanity checking on sprintf src & dest to
gcc/ChangeLog: PR middle-end/49905 * Makefile.in (OBJS): Add gimple-ssa-sprintf.o. * config/linux.h (TARGET_PRINTF_POINTER_FORMAT): Redefine. * config/linux.c (gnu_libc_printf_pointer_format): New function. * config/sol2.h (TARGET_PRINTF_POINTER_FORMAT): Same. * config/sol2.c (solaris_printf_pointer_format): New function. * doc/invoke.texi (-Wformat-length, -fprintf-return-value): New options. * doc/tm.texi.in (TARGET_PRINTF_POINTER_FORMAT): Document. * doc/tm.texi: Regenerate. * gimple-fold.h (get_range_strlen): New function. (get_maxval_strlen): Declare existing function. * gimple-fold.c (get_range_strlen): Add arguments and compute both maximum and minimum. (get_range_strlen): Define overload. (get_maxval_strlen): Adjust. * gimple-ssa-sprintf.c: New file and pass. * passes.def (pass_sprintf_length): Add new pass. * targhooks.h (default_printf_pointer_format): Declare new function. (gnu_libc_printf_pointer_format): Same. (solaris_libc_printf_pointer_format): Same. * targhooks.c (default_printf_pointer_format): Define new function. * tree-pass.h (make_pass_sprintf_length): Declare new function. * print-tree.c: Increase buffer size. gcc/c-family/ChangeLog: PR middle-end/49905 * c.opt: Add -Wformat-length and -fprintf-return-value. gcc/testsuite/ChangeLog: PR middle-end/49905 * gcc.dg/builtin-stringop-chk-1.c: Adjust. * gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: New test. * gcc.dg/tree-ssa/builtin-sprintf-warn-2.c: New test. * gcc.dg/tree-ssa/builtin-sprintf-warn-3.c: New test. * gcc.dg/tree-ssa/builtin-sprintf-warn-4.c: New test. * gcc.dg/tree-ssa/builtin-sprintf.c: New test. * gcc.dg/tree-ssa/builtin-sprintf-2.c: New test. From-SVN: r240298
This commit is contained in:
parent
6283a8db1f
commit
88d0c3f0a1
@ -1,3 +1,30 @@
|
||||
2016-09-20 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR middle-end/49905
|
||||
* Makefile.in (OBJS): Add gimple-ssa-sprintf.o.
|
||||
* config/linux.h (TARGET_PRINTF_POINTER_FORMAT): Redefine.
|
||||
* config/linux.c (gnu_libc_printf_pointer_format): New function.
|
||||
* config/sol2.h (TARGET_PRINTF_POINTER_FORMAT): Same.
|
||||
* config/sol2.c (solaris_printf_pointer_format): New function.
|
||||
* doc/invoke.texi (-Wformat-length, -fprintf-return-value): New
|
||||
options.
|
||||
* doc/tm.texi.in (TARGET_PRINTF_POINTER_FORMAT): Document.
|
||||
* doc/tm.texi: Regenerate.
|
||||
* gimple-fold.h (get_range_strlen): New function.
|
||||
(get_maxval_strlen): Declare existing function.
|
||||
* gimple-fold.c (get_range_strlen): Add arguments and compute both
|
||||
maximum and minimum.
|
||||
(get_range_strlen): Define overload.
|
||||
(get_maxval_strlen): Adjust.
|
||||
* gimple-ssa-sprintf.c: New file and pass.
|
||||
* passes.def (pass_sprintf_length): Add new pass.
|
||||
* targhooks.h (default_printf_pointer_format): Declare new function.
|
||||
(gnu_libc_printf_pointer_format): Same.
|
||||
(solaris_libc_printf_pointer_format): Same.
|
||||
* targhooks.c (default_printf_pointer_format): Define new function.
|
||||
* tree-pass.h (make_pass_sprintf_length): Declare new function.
|
||||
* print-tree.c: Increase buffer size.
|
||||
|
||||
2016-09-21 Kugan Vivekanandarajah <kuganv@linaro.org>
|
||||
|
||||
* tree-vrp.c (get_value_range): Teach PARM_DECL to use ipa-vrp
|
||||
|
@ -1296,6 +1296,7 @@ OBJS = \
|
||||
gimple-ssa-nonnull-compare.o \
|
||||
gimple-ssa-split-paths.o \
|
||||
gimple-ssa-strength-reduction.o \
|
||||
gimple-ssa-sprintf.o \
|
||||
gimple-streamer-in.o \
|
||||
gimple-streamer-out.o \
|
||||
gimple-walk.o \
|
||||
|
@ -1,3 +1,8 @@
|
||||
2016-09-20 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR middle-end/49905
|
||||
* c.opt: Add -Wformat-length and -fprintf-return-value.
|
||||
|
||||
2016-09-19 Bernd Edlinger <bernd.edlinger@hotmail.de>
|
||||
|
||||
PR c++/77434
|
||||
|
@ -478,6 +478,11 @@ Wformat-extra-args
|
||||
C ObjC C++ ObjC++ Var(warn_format_extra_args) Warning LangEnabledBy(C ObjC C++ ObjC++,Wformat=, warn_format >= 1, 0)
|
||||
Warn if passing too many arguments to a function for its format string.
|
||||
|
||||
Wformat-length
|
||||
C ObjC C++ ObjC++ Warning Alias(Wformat-length=, 1, 0)
|
||||
Warn about function calls with format strings that write past the end
|
||||
of the destination region. Same as -Wformat-length=1.
|
||||
|
||||
Wformat-nonliteral
|
||||
C ObjC C++ ObjC++ Var(warn_format_nonliteral) Warning LangEnabledBy(C ObjC C++ ObjC++,Wformat=, warn_format >= 2, 0)
|
||||
Warn about format strings that are not literals.
|
||||
@ -502,6 +507,11 @@ Wformat=
|
||||
C ObjC C++ ObjC++ Joined RejectNegative UInteger Var(warn_format) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall, 1, 0)
|
||||
Warn about printf/scanf/strftime/strfmon format string anomalies.
|
||||
|
||||
Wformat-length=
|
||||
C ObjC C++ ObjC++ Joined RejectNegative UInteger Var(warn_format_length) Warning LangEnabledBy(C ObjC C++ ObjC++,Wformat=, warn_format >= 1, 0)
|
||||
Warn about function calls with format strings that write past the end
|
||||
of the destination region.
|
||||
|
||||
Wignored-qualifiers
|
||||
C C++ Var(warn_ignored_qualifiers) Warning EnabledBy(Wextra)
|
||||
Warn whenever type qualifiers are ignored.
|
||||
@ -1487,6 +1497,10 @@ fpretty-templates
|
||||
C++ ObjC++ Var(flag_pretty_templates) Init(1)
|
||||
-fno-pretty-templates Do not pretty-print template specializations as the template signature followed by the arguments.
|
||||
|
||||
fprintf-return-value
|
||||
C ObjC C++ ObjC++ LTO Optimization Var(flag_printf_return_value) Init(1)
|
||||
Treat known sprintf return values as constants.
|
||||
|
||||
freplace-objc-classes
|
||||
ObjC ObjC++ LTO Var(flag_replace_objc_classes)
|
||||
Used in Fix-and-Continue mode to indicate that object files may be swapped in at runtime.
|
||||
|
@ -21,8 +21,12 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "tm.h"
|
||||
#include "tree.h"
|
||||
#include "linux-protos.h"
|
||||
|
||||
#undef TARGET_PRINTF_POINTER_FORMAT
|
||||
#define TARGET_PRINTF_POINTER_FORMAT gnu_libc_printf_pointer_format
|
||||
|
||||
bool
|
||||
linux_libc_has_function (enum function_class fn_class)
|
||||
{
|
||||
@ -36,3 +40,16 @@ linux_libc_has_function (enum function_class fn_class)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Glibc formats pointers as if by "%zx" except for the null pointer
|
||||
which outputs "(nil)". It ignores the pound ('#') format flag but
|
||||
interprets the space and plus flags the same as in the integer
|
||||
directive. */
|
||||
|
||||
const char*
|
||||
gnu_libc_printf_pointer_format (tree arg, const char **flags)
|
||||
{
|
||||
*flags = " +";
|
||||
|
||||
return arg && integer_zerop (arg) ? "(nil)" : "%#zx";
|
||||
}
|
||||
|
@ -208,3 +208,7 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
||||
# define TARGET_LIBC_HAS_FUNCTION linux_libc_has_function
|
||||
|
||||
#endif
|
||||
|
||||
/* The format string to which "%p" corresponds. */
|
||||
#undef TARGET_PRINTF_POINTER_FORMAT
|
||||
#define TARGET_PRINTF_POINTER_FORMAT gnu_libc_printf_pointer_format
|
||||
|
@ -30,6 +30,9 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "varasm.h"
|
||||
#include "output.h"
|
||||
|
||||
#undef TARGET_PRINTF_POINTER_FORMAT
|
||||
#define TARGET_PRINTF_POINTER_FORMAT solaris_printf_pointer_format
|
||||
|
||||
tree solaris_pending_aligns, solaris_pending_inits, solaris_pending_finis;
|
||||
|
||||
/* Attach any pending attributes for DECL to the list in *ATTRIBUTES.
|
||||
@ -297,3 +300,14 @@ solaris_override_options (void)
|
||||
if (!HAVE_LD_EH_FRAME_CIEV3 && !global_options_set.x_dwarf_version)
|
||||
dwarf_version = 2;
|
||||
}
|
||||
|
||||
/* Solaris libc formats pointers as if by "%zx" with the pound ('#')
|
||||
format flag having the same meaning as in the integer directive. */
|
||||
|
||||
const char*
|
||||
solaris_printf_pointer_format (tree, const char **flags)
|
||||
{
|
||||
*flags = "#";
|
||||
|
||||
return "%zx";
|
||||
}
|
||||
|
@ -440,6 +440,10 @@ along with GCC; see the file COPYING3. If not see
|
||||
#undef TARGET_LIBC_HAS_FUNCTION
|
||||
#define TARGET_LIBC_HAS_FUNCTION default_libc_has_function
|
||||
|
||||
/* The format string to which "%p" corresponds. */
|
||||
#undef TARGET_LIBC_PRINTF_POINTER_FORMAT
|
||||
#define TARGET_LIBC_PRINTF_POINTER_FORMAT solaris_libc_printf_pointer_format
|
||||
|
||||
extern GTY(()) tree solaris_pending_aligns;
|
||||
extern GTY(()) tree solaris_pending_inits;
|
||||
extern GTY(()) tree solaris_pending_finis;
|
||||
|
@ -268,7 +268,8 @@ Objective-C and Objective-C++ Dialects}.
|
||||
-Wno-div-by-zero -Wdouble-promotion -Wduplicated-cond @gol
|
||||
-Wempty-body -Wenum-compare -Wno-endif-labels @gol
|
||||
-Werror -Werror=* -Wfatal-errors -Wfloat-equal -Wformat -Wformat=2 @gol
|
||||
-Wno-format-contains-nul -Wno-format-extra-args -Wformat-nonliteral @gol
|
||||
-Wno-format-contains-nul -Wno-format-extra-args -Wformat-length=@var{n} @gol
|
||||
-Wformat-nonliteral @gol
|
||||
-Wformat-security -Wformat-signedness -Wformat-y2k -Wframe-address @gol
|
||||
-Wframe-larger-than=@var{len} -Wno-free-nonheap-object -Wjump-misses-init @gol
|
||||
-Wignored-qualifiers -Wignored-attributes -Wincompatible-pointer-types @gol
|
||||
@ -379,7 +380,7 @@ Objective-C and Objective-C++ Dialects}.
|
||||
-fno-toplevel-reorder -fno-trapping-math -fno-zero-initialized-in-bss @gol
|
||||
-fomit-frame-pointer -foptimize-sibling-calls @gol
|
||||
-fpartial-inlining -fpeel-loops -fpredictive-commoning @gol
|
||||
-fprefetch-loop-arrays @gol
|
||||
-fprefetch-loop-arrays -fprintf-return-value @gol
|
||||
-fprofile-correction @gol
|
||||
-fprofile-use -fprofile-use=@var{path} -fprofile-values @gol
|
||||
-fprofile-reorder-functions @gol
|
||||
@ -3888,6 +3889,88 @@ in the case of @code{scanf} formats, this option suppresses the
|
||||
warning if the unused arguments are all pointers, since the Single
|
||||
Unix Specification says that such unused arguments are allowed.
|
||||
|
||||
@item -Wformat-length
|
||||
@itemx -Wformat-length=@var{level}
|
||||
@opindex Wformat-length
|
||||
@opindex Wno-format-length
|
||||
Warn about calls to formatted input/output functions such as @code{sprintf}
|
||||
that might overflow the destination buffer, or about bounded functions such
|
||||
as @code{snprintf} that might result in output truncation. When the exact
|
||||
number of bytes written by a format directive cannot be determined at
|
||||
compile-time it is estimated based on heuristics that depend on the
|
||||
@var{level} argument and on optimization. While enabling optimization
|
||||
will in most cases improve the accuracy of the warning, it may also
|
||||
result in false positives.
|
||||
|
||||
@table @gcctabopt
|
||||
@item -Wformat-length
|
||||
@item -Wformat-length=1
|
||||
@opindex Wformat-length
|
||||
@opindex Wno-format-length
|
||||
Level @var{1} of @option{-Wformat-length} enabled by @option{-Wformat}
|
||||
employs a conservative approach that warns only about calls that most
|
||||
likely overflow the buffer or result in output truncation. At this
|
||||
level, numeric arguments to format directives with unknown values are
|
||||
assumed to have the value of one, and strings of unknown length to be
|
||||
empty. Numeric arguments that are known to be bounded to a subrange
|
||||
of their type, or string arguments whose output is bounded either by
|
||||
their directive's precision or by a finite set of string literals, are
|
||||
assumed to take on the value within the range that results in the most
|
||||
bytes on output. For example, the call to @code{sprintf} below is
|
||||
diagnosed because even with both @var{a} and @var{b} equal to zero,
|
||||
the terminating NUL character (@code{'\0'}) appended by the function
|
||||
to the destination buffer will be written past its end. Increasing
|
||||
the size of the buffer by a single byte is sufficient to avoid the
|
||||
warning, though it may not be sufficient to avoid the overflow.
|
||||
|
||||
@smallexample
|
||||
void f (int a, int b)
|
||||
@{
|
||||
char buf [12];
|
||||
sprintf (buf, "a = %i, b = %i\n", a, b);
|
||||
@}
|
||||
@end smallexample
|
||||
|
||||
@item -Wformat-length=2
|
||||
Level @var{2} warns also about calls that might overflow the destination
|
||||
buffer or result in truncation given an argument of sufficient length
|
||||
or magnitude. At level @var{2}, unknown numeric arguments are assumed
|
||||
to have the minimum representable value for signed types with a precision
|
||||
greater than 1, and the maximum representable value otherwise. Unknown
|
||||
string arguments whose length cannot be assumed to be bounded either by
|
||||
the directive's precision, or by a finite set of string literals they
|
||||
may evaluate to, or the character array they may point to, are assumed
|
||||
to be 1 character long.
|
||||
|
||||
At level @var{2}, the call in the example above is again diagnosed, but
|
||||
this time because with @var{a} equal to a 32-bit @code{INT_MIN} the first
|
||||
@code{%i} directive will write some of its digits beyond the end of
|
||||
the destination buffer. To make the call safe regardless of the values
|
||||
of the two variables, the size of the destination buffer must be increased
|
||||
to at least 34 bytes. GCC includes the minimum size of the buffer in
|
||||
an informational note following the warning.
|
||||
|
||||
An alternative to increasing the size of the destination buffer is to
|
||||
constrain the range of formatted values. The maximum length of string
|
||||
arguments can be bounded by specifying the precision in the format
|
||||
directive. When numeric arguments of format directives can be assumed
|
||||
to be bounded by less than the precision of their type, choosing
|
||||
an appropriate length modifier to the format specifier will reduce
|
||||
the required buffer size. For example, if @var{a} and @var{b} in the
|
||||
example above can be assumed to be within the precision of
|
||||
the @code{short int} type then using either the @code{%hi} format
|
||||
directive or casting the argument to @code{short} reduces the maximum
|
||||
required size of the buffer to 24 bytes.
|
||||
|
||||
@smallexample
|
||||
void f (int a, int b)
|
||||
@{
|
||||
char buf [23];
|
||||
sprintf (buf, "a = %hi, b = %i\n", a, (short)b);
|
||||
@}
|
||||
@end smallexample
|
||||
@end table
|
||||
|
||||
@item -Wno-format-zero-length
|
||||
@opindex Wno-format-zero-length
|
||||
@opindex Wformat-zero-length
|
||||
@ -7855,6 +7938,30 @@ dependent on the structure of loops within the source code.
|
||||
|
||||
Disabled at level @option{-Os}.
|
||||
|
||||
@item -fprintf-return-value
|
||||
@opindex fprintf-return-value
|
||||
Substitute constants for known return value of formatted output functions
|
||||
such as @code{sprintf}, @code{snprintf}, @code{vsprintf}, and @code{vsnprintf}
|
||||
(but not @code{printf} of @code{fprintf}). This transformation allows GCC
|
||||
to optimize or even eliminate branches based on the known return value of
|
||||
these functions called with arguments that are either constant, or whose
|
||||
values are known to be in a range that makes determining the exact return
|
||||
value possible. For example, both the branch and the body of the @code{if}
|
||||
statement (but not the call to @code{snprint}) can be optimized away when
|
||||
@code{i} is a 32-bit or smaller integer because the return value is guaranteed
|
||||
to be at most 8.
|
||||
|
||||
@smallexample
|
||||
char buf[9];
|
||||
if (snprintf (buf, "%08x", i) >= sizeof buf)
|
||||
@dots{}
|
||||
@end smallexample
|
||||
|
||||
The @option{-fprintf-return-value} option relies on other optimizations
|
||||
and yields best results with @option{-O2}. It works in tandem with the
|
||||
@option{-Wformat-length} option. The @option{-fprintf-return-value}
|
||||
option is disabled by default.
|
||||
|
||||
@item -fno-peephole
|
||||
@itemx -fno-peephole2
|
||||
@opindex fno-peephole
|
||||
|
@ -5332,6 +5332,10 @@ In either case, it remains possible to select code-generation for the alternate
|
||||
scheme, by means of compiler command line switches.
|
||||
@end defmac
|
||||
|
||||
@deftypefn {Target Hook} {const char*} TARGET_PRINTF_POINTER_FORMAT (tree, const char **@var{flags})
|
||||
Determine the target @code{printf} implementation format string that the most closely corresponds to the @code{%p} format directive. The object pointed to by the @var{flags} is set to a string consisting of recognized format flags such as the @code{'#'} character.
|
||||
@end deftypefn
|
||||
|
||||
@node Addressing Modes
|
||||
@section Addressing Modes
|
||||
@cindex addressing modes
|
||||
|
@ -4065,6 +4065,8 @@ In either case, it remains possible to select code-generation for the alternate
|
||||
scheme, by means of compiler command line switches.
|
||||
@end defmac
|
||||
|
||||
@hook TARGET_PRINTF_POINTER_FORMAT
|
||||
|
||||
@node Addressing Modes
|
||||
@section Addressing Modes
|
||||
@cindex addressing modes
|
||||
|
@ -1159,21 +1159,30 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len)
|
||||
}
|
||||
|
||||
|
||||
/* 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. */
|
||||
/* Obtain the minimum and maximum string length or minimum and maximum
|
||||
value of ARG in LENGTH[0] and LENGTH[1], respectively.
|
||||
If ARG is an SSA name variable, follow its use-def chains. When
|
||||
TYPE == 0, if LENGTH[1] 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 obtained, 1 for maximum string
|
||||
length and 2 for maximum value ARG can have.
|
||||
When FUZZY is set and the length of a string cannot be determined,
|
||||
the function instead considers as the maximum possible length the
|
||||
size of a character array it may refer to. */
|
||||
|
||||
static bool
|
||||
get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
|
||||
get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
|
||||
bool fuzzy)
|
||||
{
|
||||
tree var, val;
|
||||
gimple *def_stmt;
|
||||
|
||||
/* The minimum and maximum length. The MAXLEN pointer stays unchanged
|
||||
but MINLEN may be cleared during the execution of the function. */
|
||||
tree *minlen = length;
|
||||
tree *const maxlen = length + 1;
|
||||
|
||||
if (TREE_CODE (arg) != SSA_NAME)
|
||||
{
|
||||
/* We can end up with &(*iftmp_1)[0] here as well, so handle it. */
|
||||
@ -1184,8 +1193,8 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
|
||||
tree aop0 = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
|
||||
if (TREE_CODE (aop0) == INDIRECT_REF
|
||||
&& TREE_CODE (TREE_OPERAND (aop0, 0)) == SSA_NAME)
|
||||
return get_maxval_strlen (TREE_OPERAND (aop0, 0),
|
||||
length, visited, type);
|
||||
return get_range_strlen (TREE_OPERAND (aop0, 0),
|
||||
length, visited, type, fuzzy);
|
||||
}
|
||||
|
||||
if (type == 2)
|
||||
@ -1197,26 +1206,60 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
|
||||
}
|
||||
else
|
||||
val = c_strlen (arg, 1);
|
||||
|
||||
if (!val && fuzzy)
|
||||
{
|
||||
if (TREE_CODE (arg) == ADDR_EXPR)
|
||||
return get_range_strlen (TREE_OPERAND (arg, 0), length,
|
||||
visited, type, fuzzy);
|
||||
|
||||
if (TREE_CODE (arg) == COMPONENT_REF
|
||||
&& TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 1))) == ARRAY_TYPE)
|
||||
{
|
||||
/* Use the type of the member array to determine the upper
|
||||
bound on the length of the array. This may be overly
|
||||
optimistic if the array itself isn't NUL-terminated and
|
||||
the caller relies on the subsequent member to contain
|
||||
the NUL. */
|
||||
arg = TREE_OPERAND (arg, 1);
|
||||
val = TYPE_SIZE_UNIT (TREE_TYPE (arg));
|
||||
if (!val || integer_zerop (val))
|
||||
return false;
|
||||
val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
|
||||
integer_one_node);
|
||||
/* Avoid using the array size as the minimum. */
|
||||
minlen = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!val)
|
||||
return false;
|
||||
|
||||
if (*length)
|
||||
if (minlen
|
||||
&& (!*minlen
|
||||
|| (type > 0
|
||||
&& TREE_CODE (*minlen) == INTEGER_CST
|
||||
&& TREE_CODE (val) == INTEGER_CST
|
||||
&& tree_int_cst_lt (val, *minlen))))
|
||||
*minlen = val;
|
||||
|
||||
if (*maxlen)
|
||||
{
|
||||
if (type > 0)
|
||||
{
|
||||
if (TREE_CODE (*length) != INTEGER_CST
|
||||
if (TREE_CODE (*maxlen) != INTEGER_CST
|
||||
|| TREE_CODE (val) != INTEGER_CST)
|
||||
return false;
|
||||
|
||||
if (tree_int_cst_lt (*length, val))
|
||||
*length = val;
|
||||
if (tree_int_cst_lt (*maxlen, val))
|
||||
*maxlen = val;
|
||||
return true;
|
||||
}
|
||||
else if (simple_cst_equal (val, *length) != 1)
|
||||
else if (simple_cst_equal (val, *maxlen) != 1)
|
||||
return false;
|
||||
}
|
||||
|
||||
*length = val;
|
||||
*maxlen = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1244,14 +1287,14 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
|
||||
|| gimple_assign_unary_nop_p (def_stmt))
|
||||
{
|
||||
tree rhs = gimple_assign_rhs1 (def_stmt);
|
||||
return get_maxval_strlen (rhs, length, visited, type);
|
||||
return get_range_strlen (rhs, length, visited, type, fuzzy);
|
||||
}
|
||||
else if (gimple_assign_rhs_code (def_stmt) == COND_EXPR)
|
||||
{
|
||||
tree op2 = gimple_assign_rhs2 (def_stmt);
|
||||
tree op3 = gimple_assign_rhs3 (def_stmt);
|
||||
return get_maxval_strlen (op2, length, visited, type)
|
||||
&& get_maxval_strlen (op3, length, visited, type);
|
||||
return get_range_strlen (op2, length, visited, type, fuzzy)
|
||||
&& get_range_strlen (op3, length, visited, type, fuzzy);
|
||||
}
|
||||
return false;
|
||||
|
||||
@ -1274,8 +1317,13 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
|
||||
if (arg == gimple_phi_result (def_stmt))
|
||||
continue;
|
||||
|
||||
if (!get_maxval_strlen (arg, length, visited, type))
|
||||
return false;
|
||||
if (!get_range_strlen (arg, length, visited, type, fuzzy))
|
||||
{
|
||||
if (fuzzy)
|
||||
*maxlen = build_all_ones_cst (size_type_node);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -1285,17 +1333,40 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine the minimum and maximum value or string length that ARG
|
||||
refers to and store each in the first two elements of MINMAXLEN.
|
||||
For expressions that point to strings of unknown lengths that are
|
||||
character arrays, use the upper bound of the array as the maximum
|
||||
length. For example, given an expression like 'x ? array : "xyz"'
|
||||
and array declared as 'char array[8]', MINMAXLEN[0] will be set
|
||||
to 3 and MINMAXLEN[1] to 7, the longest string that could be
|
||||
stored in array.
|
||||
*/
|
||||
|
||||
void get_range_strlen (tree arg, tree minmaxlen[2])
|
||||
{
|
||||
bitmap visited = NULL;
|
||||
|
||||
minmaxlen[0] = NULL_TREE;
|
||||
minmaxlen[1] = NULL_TREE;
|
||||
|
||||
get_range_strlen (arg, minmaxlen, &visited, 1, true);
|
||||
|
||||
if (visited)
|
||||
BITMAP_FREE (visited);
|
||||
}
|
||||
|
||||
tree
|
||||
get_maxval_strlen (tree arg, int type)
|
||||
{
|
||||
bitmap visited = NULL;
|
||||
tree len = NULL_TREE;
|
||||
if (!get_maxval_strlen (arg, &len, &visited, type))
|
||||
len = NULL_TREE;
|
||||
tree len[2] = { NULL_TREE, NULL_TREE };
|
||||
if (!get_range_strlen (arg, len, &visited, type, false))
|
||||
len[1] = NULL_TREE;
|
||||
if (visited)
|
||||
BITMAP_FREE (visited);
|
||||
|
||||
return len;
|
||||
return len[1];
|
||||
}
|
||||
|
||||
|
||||
|
@ -24,6 +24,8 @@ along with GCC; see the file COPYING3. If not see
|
||||
|
||||
extern tree canonicalize_constructor_val (tree, tree);
|
||||
extern tree get_symbol_constant_value (tree);
|
||||
extern void get_range_strlen (tree, tree[2]);
|
||||
extern tree get_maxval_strlen (tree, int);
|
||||
extern void gimplify_and_update_call_from_tree (gimple_stmt_iterator *, tree);
|
||||
extern bool fold_stmt (gimple_stmt_iterator *);
|
||||
extern bool fold_stmt (gimple_stmt_iterator *, tree (*) (tree));
|
||||
|
2686
gcc/gimple-ssa-sprintf.c
Normal file
2686
gcc/gimple-ssa-sprintf.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -43,6 +43,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
NEXT_PASS (pass_warn_function_return);
|
||||
NEXT_PASS (pass_expand_omp);
|
||||
NEXT_PASS (pass_build_cgraph_edges);
|
||||
NEXT_PASS (pass_sprintf_length, false);
|
||||
TERMINATE_PASS_LIST (all_lowering_passes)
|
||||
|
||||
/* Interprocedural optimization passes. */
|
||||
@ -306,6 +307,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
NEXT_PASS (pass_simduid_cleanup);
|
||||
NEXT_PASS (pass_lower_vector_ssa);
|
||||
NEXT_PASS (pass_cse_reciprocals);
|
||||
NEXT_PASS (pass_sprintf_length, true);
|
||||
NEXT_PASS (pass_reassoc, false /* insert_powi_p */);
|
||||
NEXT_PASS (pass_strength_reduction);
|
||||
NEXT_PASS (pass_split_paths);
|
||||
|
@ -769,7 +769,8 @@ print_node (FILE *file, const char *prefix, tree node, int indent)
|
||||
|
||||
case VECTOR_CST:
|
||||
{
|
||||
char buf[10];
|
||||
/* Big enough for 2 UINT_MAX plus the string below. */
|
||||
char buf[32];
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < VECTOR_CST_NELTS (node); ++i)
|
||||
|
@ -3353,6 +3353,12 @@ greater than 128 and a multiple of 32.",
|
||||
machine_mode, (int n, bool extended),
|
||||
default_floatn_mode)
|
||||
|
||||
DEFHOOK
|
||||
(printf_pointer_format,
|
||||
"Determine the target @code{printf} implementation format string that the most closely corresponds to the @code{%p} format directive. The object pointed to by the @var{flags} is set to a string consisting of recognized format flags such as the @code{'#'} character.",
|
||||
const char*, (tree, const char **flags),
|
||||
default_printf_pointer_format)
|
||||
|
||||
/* Compute cost of moving data from a register of class FROM to one of
|
||||
TO, using MODE. */
|
||||
DEFHOOK
|
||||
|
@ -1509,6 +1509,20 @@ no_c99_libc_has_function (enum function_class fn_class ATTRIBUTE_UNUSED)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return the format string to which "%p" corresponds. By default,
|
||||
assume it corresponds to the C99 "%zx" format and set *FLAGS to
|
||||
the empty string to indicate that format flags have no effect.
|
||||
An example of an implementation that matches this description
|
||||
is AIX. */
|
||||
|
||||
const char*
|
||||
default_printf_pointer_format (tree, const char **flags)
|
||||
{
|
||||
*flags = "";
|
||||
|
||||
return "%zx";
|
||||
}
|
||||
|
||||
tree
|
||||
default_builtin_tm_load_store (tree ARG_UNUSED (type))
|
||||
{
|
||||
|
@ -191,6 +191,10 @@ extern bool default_libc_has_function (enum function_class);
|
||||
extern bool no_c99_libc_has_function (enum function_class);
|
||||
extern bool gnu_libc_has_function (enum function_class);
|
||||
|
||||
extern const char* default_printf_pointer_format (tree, const char **);
|
||||
extern const char* gnu_libc_printf_pointer_format (tree, const char **);
|
||||
extern const char* solaris_printf_pointer_format (tree, const char **);
|
||||
|
||||
extern tree default_builtin_tm_load_store (tree);
|
||||
|
||||
extern int default_memory_move_cost (machine_mode, reg_class_t, bool);
|
||||
|
@ -1,3 +1,14 @@
|
||||
2016-09-20 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR middle-end/49905
|
||||
* gcc.dg/builtin-stringop-chk-1.c: Adjust.
|
||||
* gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: New test.
|
||||
* gcc.dg/tree-ssa/builtin-sprintf-warn-2.c: New test.
|
||||
* gcc.dg/tree-ssa/builtin-sprintf-warn-3.c: New test.
|
||||
* gcc.dg/tree-ssa/builtin-sprintf-warn-4.c: New test.
|
||||
* gcc.dg/tree-ssa/builtin-sprintf.c: New test.
|
||||
* gcc.dg/tree-ssa/builtin-sprintf-2.c: New test.
|
||||
|
||||
2016-09-21 Kugan Vivekanandarajah <kuganv@linaro.org>
|
||||
|
||||
* gcc.dg/guality/pr54519-1.c: Add -fno-ipa-vrp. Else constant
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* Test whether buffer overflow warnings for __*_chk builtins
|
||||
are emitted properly. */
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -std=gnu99 -ftrack-macro-expansion=0" } */
|
||||
/* { dg-options "-O2 -Wno-format -std=gnu99 -ftrack-macro-expansion=0" } */
|
||||
/* { dg-additional-options "-mstructure-size-boundary=8" { target arm*-*-* } } */
|
||||
// { dg-skip-if "packed attribute missing for t" { "epiphany-*-*" } { "*" } { "" } }
|
||||
|
||||
|
218
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-2.c
Normal file
218
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-2.c
Normal file
@ -0,0 +1,218 @@
|
||||
/* Test to verify that the return value of calls to __builtin_sprintf
|
||||
is not folded if the call has undefined behavior even if it would
|
||||
otherwise produce a known number of bytes on output, and that if
|
||||
the return value is in a known range the range is not made
|
||||
available to subsequent passes and doesn't affect branching and
|
||||
the removal of code.
|
||||
The test is compiled with warnings disabled to make sure the absence
|
||||
of optimizations does not depend on the presence of warnings. */
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -fprintf-return-value -fdump-tree-optimized -ftrack-macro-expansion=0 -w" } */
|
||||
|
||||
#ifndef LINE
|
||||
# define LINE 0
|
||||
#endif
|
||||
|
||||
#define INT_MAX __INT_MAX__
|
||||
|
||||
char *buf;
|
||||
char buf8k [8192];
|
||||
|
||||
#define concat(a, b) a ## b
|
||||
#define CAT(a, b) concat (a, b)
|
||||
|
||||
#define EQL(expect, size, fmt, ...) \
|
||||
void CAT (test_on_line_, __LINE__)(void) \
|
||||
{ \
|
||||
if (!LINE || LINE == __LINE__) \
|
||||
{ \
|
||||
char *dst = size < 0 ? buf : buf8k + sizeof buf8k - size; \
|
||||
int result = __builtin_sprintf (dst, fmt, __VA_ARGS__); \
|
||||
if (result != expect) \
|
||||
__builtin_abort (); \
|
||||
} \
|
||||
}
|
||||
|
||||
/* Verify that the return value or range or return values from the call
|
||||
to the formatted function is not treated as a constant or made available
|
||||
to subsequent optimization passes. */
|
||||
#define RNG(min, max, size, fmt, ...) \
|
||||
void CAT (test_on_line_, __LINE__)(void) \
|
||||
{ \
|
||||
if (!LINE || LINE == __LINE__) \
|
||||
{ \
|
||||
char *dst = size < 0 ? buf : buf8k + sizeof buf8k - size; \
|
||||
int result = __builtin_sprintf (dst, fmt, __VA_ARGS__); \
|
||||
if (result < min || max < result) \
|
||||
__builtin_abort (); \
|
||||
} \
|
||||
}
|
||||
|
||||
extern int i;
|
||||
extern long li;
|
||||
extern char *str;
|
||||
|
||||
/* Verify that overflowing the destination object disables the return
|
||||
value optimization. */
|
||||
EQL (0, 0, "%c", ' ');
|
||||
EQL (0, 0, "%c", i)
|
||||
EQL (0, 0, "%-s", "");
|
||||
|
||||
EQL (1, 1, "%c", 'x');
|
||||
EQL (1, 1, "%-s", "x");
|
||||
|
||||
EQL (4, 4, "%4c", 'x');
|
||||
|
||||
/* Verify that exceeding the environmental limit of 4095 bytes for
|
||||
a single conversion specification disables the return value
|
||||
folding. */
|
||||
EQL ( 4096, sizeof buf8k, "%4096c", 'x');
|
||||
|
||||
EQL (INT_MAX, -1, "%*c", INT_MAX, 'x');
|
||||
|
||||
EQL ( 4096, sizeof buf8k, "%4096.4094f", 1.0);
|
||||
EQL ( 4096, sizeof buf8k, "%.4094f", 1.0);
|
||||
EQL ( 4097, sizeof buf8k, "%.4095f", 1.0);
|
||||
|
||||
enum { imax2 = (INT_MAX / 2) * 2 };
|
||||
EQL (imax2, -1, "%*c%*c", INT_MAX / 2, 'x', INT_MAX / 2, 'y');
|
||||
|
||||
/* Verify that range inforation for calls that overflow the destination
|
||||
isn't available. */
|
||||
RNG (0, 0, 0, "%hhi", i)
|
||||
RNG (0, 0, 1, "%hhi", i)
|
||||
RNG (0, 1, 1, "%hhi", i)
|
||||
RNG (0, 0, 2, "%hhi", i)
|
||||
RNG (0, 1, 2, "%hhi", i)
|
||||
RNG (0, 2, 2, "%hhi", i)
|
||||
RNG (0, 0, 3, "%hhi", i)
|
||||
RNG (0, 1, 3, "%hhi", i)
|
||||
RNG (0, 2, 3, "%hhi", i)
|
||||
RNG (0, 3, 3, "%hhi", i)
|
||||
RNG (0, 0, 4, "%hhi", i)
|
||||
RNG (0, 1, 4, "%hhi", i)
|
||||
RNG (0, 2, 4, "%hhi", i)
|
||||
RNG (0, 3, 4, "%hhi", i)
|
||||
RNG (0, 4, 4, "%hhi", i)
|
||||
|
||||
RNG (0, 0, 0, "%hhu", i)
|
||||
RNG (0, 0, 1, "%hhu", i)
|
||||
RNG (0, 1, 1, "%hhu", i)
|
||||
RNG (0, 0, 2, "%hhu", i)
|
||||
RNG (0, 1, 2, "%hhu", i)
|
||||
RNG (0, 2, 2, "%hhu", i)
|
||||
RNG (0, 0, 3, "%hhu", i)
|
||||
RNG (0, 1, 3, "%hhu", i)
|
||||
RNG (0, 2, 3, "%hhu", i)
|
||||
RNG (0, 3, 3, "%hhu", i)
|
||||
|
||||
RNG (0, 0, 0, "%i", i)
|
||||
|
||||
RNG (0, 0, 1, "%i", i)
|
||||
RNG (0, 1, 1, "%i", i)
|
||||
|
||||
RNG (0, 0, 2, "%i", i)
|
||||
RNG (0, 1, 2, "%i", i)
|
||||
RNG (0, 2, 2, "%i", i)
|
||||
|
||||
RNG (0, 0, 3, "%i", i)
|
||||
RNG (0, 1, 3, "%i", i)
|
||||
RNG (0, 2, 3, "%i", i)
|
||||
RNG (0, 3, 3, "%i", i)
|
||||
|
||||
RNG (0, 0, 4, "%i", i)
|
||||
RNG (0, 1, 4, "%i", i)
|
||||
RNG (0, 2, 4, "%i", i)
|
||||
RNG (0, 3, 4, "%i", i)
|
||||
RNG (0, 4, 4, "%i", i)
|
||||
|
||||
RNG (0, 0, 5, "%i", i)
|
||||
RNG (0, 1, 5, "%i", i)
|
||||
RNG (0, 2, 5, "%i", i)
|
||||
RNG (0, 3, 5, "%i", i)
|
||||
RNG (0, 4, 5, "%i", i)
|
||||
RNG (0, 5, 5, "%i", i)
|
||||
|
||||
RNG (0, 0, 6, "%i", i)
|
||||
RNG (0, 1, 6, "%i", i)
|
||||
RNG (0, 2, 6, "%i", i)
|
||||
RNG (0, 3, 6, "%i", i)
|
||||
RNG (0, 4, 6, "%i", i)
|
||||
RNG (0, 5, 6, "%i", i)
|
||||
RNG (0, 6, 6, "%i", i)
|
||||
|
||||
RNG (0, 0, 7, "%i", i)
|
||||
RNG (0, 1, 7, "%i", i)
|
||||
RNG (0, 2, 7, "%i", i)
|
||||
RNG (0, 3, 7, "%i", i)
|
||||
RNG (0, 4, 7, "%i", i)
|
||||
RNG (0, 5, 7, "%i", i)
|
||||
RNG (0, 6, 7, "%i", i)
|
||||
|
||||
#if __SIZEOF_INT__ == 4
|
||||
|
||||
/* A 32-bit int takes up at most 11 bytes (-2147483648) not including
|
||||
the terminating nul. */
|
||||
RNG (0, 7, 7, "%i", i)
|
||||
|
||||
RNG (0, 0, 8, "%i", i)
|
||||
RNG (0, 1, 8, "%i", i)
|
||||
RNG (0, 2, 8, "%i", i)
|
||||
RNG (0, 3, 8, "%i", i)
|
||||
RNG (0, 4, 8, "%i", i)
|
||||
RNG (0, 5, 8, "%i", i)
|
||||
RNG (0, 6, 8, "%i", i)
|
||||
RNG (0, 7, 8, "%i", i)
|
||||
RNG (0, 8, 8, "%i", i)
|
||||
|
||||
RNG (0, 0, 9, "%i", i)
|
||||
RNG (0, 1, 9, "%i", i)
|
||||
RNG (0, 2, 9, "%i", i)
|
||||
RNG (0, 3, 9, "%i", i)
|
||||
RNG (0, 4, 9, "%i", i)
|
||||
RNG (0, 5, 9, "%i", i)
|
||||
RNG (0, 6, 9, "%i", i)
|
||||
RNG (0, 7, 9, "%i", i)
|
||||
RNG (0, 8, 9, "%i", i)
|
||||
RNG (0, 9, 9, "%i", i)
|
||||
|
||||
RNG (0, 0, 10, "%i", i)
|
||||
RNG (0, 1, 10, "%i", i)
|
||||
RNG (0, 2, 10, "%i", i)
|
||||
RNG (0, 3, 10, "%i", i)
|
||||
RNG (0, 4, 10, "%i", i)
|
||||
RNG (0, 5, 10, "%i", i)
|
||||
RNG (0, 6, 10, "%i", i)
|
||||
RNG (0, 7, 10, "%i", i)
|
||||
RNG (0, 8, 10, "%i", i)
|
||||
RNG (0, 9, 10, "%i", i)
|
||||
RNG (0, 10, 10, "%i", i)
|
||||
|
||||
#endif
|
||||
|
||||
/* Verify the result of a conditional expression involving a string
|
||||
literal and an unknown string isn't optimized. */
|
||||
RNG (0, 1, 4, "%-s", i ? str : "123");
|
||||
RNG (0, 1, 4, "%-s", i ? "123" : str);
|
||||
|
||||
/* Verify that no call to abort has been eliminated and that each call
|
||||
is at the beginning of a basic block (and thus the result of a branch).
|
||||
This latter test tries to verify that the test preceding the call to
|
||||
abort has not been eliminated either.
|
||||
|
||||
The expected output looks something like this:
|
||||
|
||||
<bb 2>:
|
||||
result_3 = __builtin_sprintf (&MEM[(void *)&buf8k + 8192B], "%c", 32);
|
||||
if (result_3 != 0)
|
||||
goto <bb 3>;
|
||||
else
|
||||
goto <bb 4>;
|
||||
|
||||
<bb 3>:
|
||||
__builtin_abort ();
|
||||
|
||||
*/
|
||||
|
||||
/* { dg-final { scan-tree-dump-times ">:\n *__builtin_abort" 105 "optimized" { target { ilp32 || lp64 } } } } */
|
||||
/* { dg-final { scan-tree-dump-times ">:\n *__builtin_abort" 74 "optimized" { target { { ! ilp32 } && { ! lp64 } } } } } */
|
1417
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-1.c
Normal file
1417
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-1.c
Normal file
File diff suppressed because it is too large
Load Diff
214
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-2.c
Normal file
214
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-2.c
Normal file
@ -0,0 +1,214 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-std=c99 -Wformat -Wformat-length=2 -ftrack-macro-expansion=0" } */
|
||||
|
||||
/* When debugging, define LINE to the line number of the test case to exercise
|
||||
and avoid exercising any of the others. The buffer and objsize macros
|
||||
below make use of LINE to avoid warnings for other lines. */
|
||||
#ifndef LINE
|
||||
# define LINE 0
|
||||
#endif
|
||||
|
||||
char buffer [256];
|
||||
extern char *ptr;
|
||||
|
||||
#define buffer(size) \
|
||||
(!LINE || __LINE__ == LINE ? buffer + sizeof buffer - size : ptr)
|
||||
|
||||
#define objsize(size) (!LINE || __LINE__ == LINE ? size : __SIZE_MAX__ / 2)
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
#if !__cplusplus
|
||||
typedef __WCHAR_TYPE__ wchar_t;
|
||||
#endif
|
||||
|
||||
typedef unsigned char UChar;
|
||||
|
||||
#define T(size, fmt, ...) \
|
||||
__builtin_sprintf (buffer (size), fmt, __VA_ARGS__)
|
||||
|
||||
__builtin_va_list va;
|
||||
|
||||
/* Exercise buffer overflow detection with const string arguments. */
|
||||
|
||||
void test_s_const (void)
|
||||
{
|
||||
/* Wide string literals are handled slightly differently than
|
||||
at level 1. At level 1, each wide character is assumed to
|
||||
convert into a single byte. At level 2, they are assumed
|
||||
to convert into at least one byte. */
|
||||
T (0, "%ls", L""); /* { dg-warning "nul past the end" } */
|
||||
T (1, "%ls", L"");
|
||||
T (1, "%ls", L"\0");
|
||||
T (1, "%1ls", L""); /* { dg-warning "nul past the end" } */
|
||||
|
||||
T (0, "%*ls", 0, L""); /* { dg-warning "nul past the end" } */
|
||||
T (1, "%*ls", 0, L"");
|
||||
T (1, "%*ls", 0, L"\0");
|
||||
T (1, "%*ls", 1, L""); /* { dg-warning "nul past the end" } */
|
||||
|
||||
T (1, "%ls", L"1"); /* { dg-warning "nul past the end" } */
|
||||
T (1, "%.0ls", L"1");
|
||||
T (2, "%.0ls", L"1");
|
||||
T (2, "%.1ls", L"1");
|
||||
|
||||
/* The "%.2ls" directive below will write at a minimum 1 byte (because
|
||||
L"1" is known and can be assumed to convert to at least one multibyte
|
||||
character), and at most 2 bytes because of the precision. Since its
|
||||
output is explicitly bounded it is diagnosed. */
|
||||
T (2, "%.2ls", L"1"); /* { dg-warning "nul past the end" } */
|
||||
T (2, "%.*ls", 2, L"1"); /* { dg-warning "nul past the end" } */
|
||||
|
||||
/* The following three are constrained by the precision to at most
|
||||
that many bytes of the converted wide string plus a terminating NUL. */
|
||||
T (2, "%.0ls", L"1");
|
||||
T (2, "%.1ls", L"1");
|
||||
T (3, "%.2ls", L"1");
|
||||
}
|
||||
|
||||
|
||||
struct Arrays {
|
||||
char a1 [1];
|
||||
char a2 [2];
|
||||
char a3 [3];
|
||||
char a4 [4];
|
||||
char a0 [0];
|
||||
char ax [];
|
||||
};
|
||||
|
||||
/* Exercise buffer overflow detection with non-const string arguments. */
|
||||
|
||||
void test_s_nonconst (const char *s, const wchar_t *ws, struct Arrays *a)
|
||||
{
|
||||
T (0, "%s", s); /* { dg-warning "into a region" "sprintf transformed into strcpy" { xfail *-*-*-* } } */
|
||||
T (1, "%s", s); /* { dg-warning "nul past the end" "sprintf transformed into strcpy" { xfail *-*-*-* } } */
|
||||
T (1, "%1s", s); /* { dg-warning "nul past the end" } */
|
||||
T (1, "%.0s", s);
|
||||
T (1, "%.1s", s); /* { dg-warning "writing a terminating nul" } */
|
||||
|
||||
T (1, "%ls", ws); /* { dg-warning "writing a terminating nul" } */
|
||||
|
||||
/* Verify that the size of the array is used in lieu of its length.
|
||||
The minus sign disables GCC's sprintf to strcpy transformation. */
|
||||
T (1, "%-s", a->a1); /* { dg-warning "nul past the end" } */
|
||||
|
||||
/* In the following test, since the length of the strings isn't known,
|
||||
their type (the array) is used to bound the maximum length to 1,
|
||||
which means the "%-s" directive would not overflow the buffer,
|
||||
but it would leave no room for the terminating nul. */
|
||||
T (1, "%-s", a->a2); /* { dg-warning "writing a terminating nul" } */
|
||||
|
||||
/* Unlike in the test above, since the length of the string is bounded
|
||||
by the array type to at most 2, the "^-s" directive is diagnosed firts,
|
||||
preventing the diagnostic about the terminatinb nul. */
|
||||
T (1, "%-s", a->a3); /* { dg-warning "directive writing between 1 and 2 bytes" } */
|
||||
|
||||
/* The length of a zero length array and flexible array member is
|
||||
unknown and at leve 2 assumed to be at least 1. */
|
||||
T (1, "%-s", a->a0); /* { dg-warning "nul past the end" } */
|
||||
T (1, "%-s", a->ax); /* { dg-warning "nul past the end" } */
|
||||
|
||||
T (2, "%-s", a->a0);
|
||||
T (2, "%-s", a->ax);
|
||||
}
|
||||
|
||||
/* Exercise buffer overflow detection with non-const integer arguments. */
|
||||
|
||||
void test_hh_nonconst (int x)
|
||||
{
|
||||
T (1, "%hhi", x); /* { dg-warning "into a region" } */
|
||||
T (2, "%hhi", x); /* { dg-warning "into a region" } */
|
||||
T (3, "%hhi", x); /* { dg-warning "into a region" } */
|
||||
T (4, "%hhi", x); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
||||
}
|
||||
|
||||
void test_h_nonconst (int x)
|
||||
{
|
||||
extern UChar uc;
|
||||
|
||||
T (1, "%hi", uc); /* { dg-warning "into a region" } */
|
||||
T (2, "%hi", uc); /* { dg-warning "into a region" } */
|
||||
/* Formatting an 8-bit unsigned char as a signed short (or any other
|
||||
type with greater precision) can write at most 3 characters. */
|
||||
T (3, "%hi", uc); /* { dg-warning "terminating nul past" } */
|
||||
T (4, "%hi", uc);
|
||||
|
||||
/* Verify that the same thing works when the int argument is cast
|
||||
to unsigned char. */
|
||||
T (1, "%hi", (UChar)x); /* { dg-warning "into a region" } */
|
||||
T (2, "%hi", (UChar)x); /* { dg-warning "into a region" } */
|
||||
T (3, "%hi", (UChar)x); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
||||
T (4, "%hi", (UChar)x);
|
||||
}
|
||||
|
||||
void test_i_nonconst (int x)
|
||||
{
|
||||
extern UChar uc;
|
||||
|
||||
T (1, "%i", uc); /* { dg-warning "into a region" } */
|
||||
T (2, "%i", uc); /* { dg-warning "into a region" } */
|
||||
T (3, "%i", uc); /* { dg-warning "terminating nul past" } */
|
||||
T (4, "%i", uc);
|
||||
|
||||
T (1, "%i", (UChar)x); /* { dg-warning "into a region" } */
|
||||
T (2, "%i", (UChar)x); /* { dg-warning "into a region" } */
|
||||
T (3, "%i", (UChar)x); /* { dg-warning "terminating nul past" } */
|
||||
T (4, "%i", (UChar)x);
|
||||
|
||||
/* Verify the same thing using a bit-field. */
|
||||
extern struct {
|
||||
unsigned int b1: 1;
|
||||
unsigned int b2: 2;
|
||||
unsigned int b3: 3;
|
||||
unsigned int b4: 4;
|
||||
int sb4: 4;
|
||||
unsigned int b5: 5;
|
||||
unsigned int b6: 6;
|
||||
unsigned int b7: 7;
|
||||
unsigned int b8: 8;
|
||||
} bf, abf[], *pbf;
|
||||
|
||||
T (1, "%i", bf.b1); /* { dg-warning "nul past the end" } */
|
||||
T (1, "%i", abf [x].b1); /* { dg-warning "nul past the end" } */
|
||||
T (1, "%i", pbf->b1); /* { dg-warning "nul past the end" } */
|
||||
/* A one bit bit-field can only be formatted as '0' or '1'. Similarly,
|
||||
two- and three-bit bit-fields can only be formatted as a single
|
||||
decimal digit. */
|
||||
T (2, "%i", bf.b1);
|
||||
T (2, "%i", abf [x].b1);
|
||||
T (2, "%i", pbf->b1);
|
||||
T (2, "%i", bf.b2);
|
||||
T (2, "%i", abf [x].b2);
|
||||
T (2, "%i", pbf->b2);
|
||||
T (2, "%i", bf.b3);
|
||||
T (2, "%i", abf [x].b3);
|
||||
T (2, "%i", pbf->b3);
|
||||
/* A four-bit bit-field can be formatted as either one or two digits. */
|
||||
T (2, "%i", bf.b4); /* { dg-warning "nul past the end" } */
|
||||
T (2, "%i", abf [x].b4); /* { dg-warning "nul past the end" } */
|
||||
T (2, "%i", pbf->b4); /* { dg-warning "nul past the end" } */
|
||||
|
||||
T (3, "%i", bf.b4);
|
||||
T (3, "%i", pbf->b4);
|
||||
T (3, "%i", bf.b5);
|
||||
T (3, "%i", pbf->b5);
|
||||
T (3, "%i", bf.b6);
|
||||
T (3, "%i", pbf->b6);
|
||||
T (3, "%i", bf.b7); /* { dg-warning "nul past the end" } */
|
||||
T (3, "%i", pbf->b7); /* { dg-warning "nul past the end" } */
|
||||
|
||||
T (1, "%i", bf.b8); /* { dg-warning "into a region" } */
|
||||
T (2, "%i", bf.b8); /* { dg-warning "into a region" } */
|
||||
/* Formatting an 8-bit unsigned char as a signed short (or any other
|
||||
type with greater precision) int can write at most 3 characters. */
|
||||
T (3, "%i", bf.b8); /* { dg-warning "terminating nul past" } */
|
||||
T (4, "%i", bf.b8);
|
||||
|
||||
T (1, "%i", bf.b8); /* { dg-warning "into a region" } */
|
||||
T (2, "%i", bf.b8); /* { dg-warning "into a region" } */
|
||||
T (3, "%i", bf.b8); /* { dg-warning "terminating nul past" } */
|
||||
|
||||
T (2, "%i", bf.sb4); /* { dg-warning "terminating nul past" } */
|
||||
T (3, "%i", bf.sb4);
|
||||
T (4, "%i", bf.sb4);
|
||||
}
|
234
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-3.c
Normal file
234
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-3.c
Normal file
@ -0,0 +1,234 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-std=c99 -O2 -Wformat -Wformat-length=1 -ftrack-macro-expansion=0" } */
|
||||
|
||||
#ifndef LINE
|
||||
# define LINE 0
|
||||
#endif
|
||||
|
||||
#define bos(x) __builtin_object_size (x, 0)
|
||||
|
||||
#define T(bufsize, fmt, ...) \
|
||||
do { \
|
||||
if (!LINE || __LINE__ == LINE) \
|
||||
{ \
|
||||
char *d = (char *)__builtin_malloc (bufsize); \
|
||||
__builtin___sprintf_chk (d, 0, bos (d), fmt, __VA_ARGS__); \
|
||||
sink (d); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
void
|
||||
sink (void*);
|
||||
|
||||
/* Identity function to verify that the checker figures out the value
|
||||
of the operand even when it's not constant (i.e., makes use of
|
||||
inlining and constant propagation information). */
|
||||
|
||||
int i (int x) { return x; }
|
||||
const char* s (const char *str) { return str; }
|
||||
|
||||
/* Function to "generate" a unique unknown number (as far as GCC can
|
||||
tell) each time it's called. It prevents the optimizer from being
|
||||
able to narrow down the ranges of possible values in test functions
|
||||
with repeated references to the same variable. */
|
||||
extern int x (void);
|
||||
|
||||
/* Verify that the checker can detect buffer overflow when the "%s"
|
||||
argument is in a known range of lengths and one or both of which
|
||||
exceed the size of the destination. */
|
||||
|
||||
void test_sprintf_chk_string (const char *s, const char *t)
|
||||
{
|
||||
#define x x ()
|
||||
|
||||
T (1, "%s", x ? "" : "1"); /* { dg-warning "nul past the end" } */
|
||||
T (1, "%s", x ? "1" : ""); /* { dg-warning "nul past the end" } */
|
||||
T (1, "%s", x ? s : "1"); /* { dg-warning "nul past the end" } */
|
||||
T (1, "%s", x ? "1" : s); /* { dg-warning "nul past the end" } */
|
||||
T (1, "%s", x ? s : t);
|
||||
|
||||
T (2, "%s", x ? "" : "1");
|
||||
T (2, "%s", x ? "" : s);
|
||||
T (2, "%s", x ? "1" : "");
|
||||
T (2, "%s", x ? s : "");
|
||||
T (2, "%s", x ? "1" : "2");
|
||||
T (2, "%s", x ? "" : "12"); /* { dg-warning "nul past the end" } */
|
||||
T (2, "%s", x ? "12" : ""); /* { dg-warning "nul past the end" } */
|
||||
|
||||
T (2, "%s", x ? "" : "123"); /* { dg-warning "into a region" } */
|
||||
T (2, "%s", x ? "123" : ""); /* { dg-warning "into a region" } */
|
||||
|
||||
#undef x
|
||||
}
|
||||
|
||||
|
||||
/* Verify that the checker makes use of integer constant propagation
|
||||
to detect buffer overflow in non-constant cases. */
|
||||
|
||||
void test_sprintf_chk_integer_value (void)
|
||||
{
|
||||
T ( 1, "%i", i ( 0)); /* { dg-warning "nul past the end" } */
|
||||
T ( 1, "%i", i ( 1)); /* { dg-warning "nul past the end" } */
|
||||
T ( 1, "%i", i ( -1)); /* { dg-warning "into a region" } */
|
||||
T ( 1, "%i_", i ( 1)); /* { dg-warning "character ._. at offset 2 past the end" } */
|
||||
T ( 1, "_%i", i ( 1)); /* { dg-warning "into a region" } */
|
||||
T ( 1, "_%i_",i ( 1)); /* { dg-warning "into a region" } */
|
||||
T ( 1, "%o", i ( 0)); /* { dg-warning "nul past the end" } */
|
||||
T ( 1, "%u", i ( 0)); /* { dg-warning "nul past the end" } */
|
||||
T ( 1, "%x", i ( 0)); /* { dg-warning "nul past the end" } */
|
||||
T ( 1, "%#x", i ( 0)); /* { dg-warning "nul past the end" } */
|
||||
T ( 1, "%x", i ( 1)); /* { dg-warning "nul past the end" } */
|
||||
T ( 1, "%#x", i ( 1)); /* { dg-warning "into a region" } */
|
||||
|
||||
T ( 2, "%i", i ( 0));
|
||||
T ( 2, "%i", i ( 1));
|
||||
T ( 2, "%i", i ( 9));
|
||||
T ( 2, "%i", i ( -1)); /* { dg-warning "nul past the end" } */
|
||||
T ( 2, "%i", i ( 10)); /* { dg-warning "nul past the end" } */
|
||||
T ( 2, "%i_", i ( 0)); /* { dg-warning "nul past the end" } */
|
||||
T ( 2, "_%i", i ( 0)); /* { dg-warning "nul past the end" } */
|
||||
T ( 2, "_%i_",i ( 0)); /* { dg-warning "character ._. at offset 3 past the end" } */
|
||||
T ( 2, "%o", i ( 1));
|
||||
T ( 2, "%o", i ( 7));
|
||||
T ( 2, "%o", i ( 010)); /* { dg-warning "nul past the end" } */
|
||||
T ( 2, "%o", i ( 0100)); /* { dg-warning "into a region" } */
|
||||
T ( 2, "%x", i ( 1));
|
||||
T ( 2, "%#x", i ( 1)); /* { dg-warning "into a region" } */
|
||||
T ( 2, "%x", i ( 0xa));
|
||||
T ( 2, "%x", i ( 0xf));
|
||||
T ( 2, "%x", i ( 0x10)); /* { dg-warning "nul past the end" } */
|
||||
T ( 2, "%x", i ( 0xff)); /* { dg-warning "nul past the end" } */
|
||||
T ( 2, "%x", i (0x1ff)); /* { dg-warning "into a region" } */
|
||||
|
||||
T ( 3, "%i", i ( 0));
|
||||
T ( 3, "%i", i ( 1));
|
||||
T ( 3, "%i", i ( 9));
|
||||
T ( 3, "%i", i ( -9));
|
||||
T ( 3, "%i", i ( 10));
|
||||
T ( 3, "%i", i ( 99));
|
||||
T ( 3, "%i", i ( -99)); /* { dg-warning "nul past the end" } */
|
||||
|
||||
T ( 3, "%i", i (99) + i (1)); /* { dg-warning "nul past the end" } */
|
||||
|
||||
T ( 8, "%8u", i ( 1)); /* { dg-warning "nul past the end" } */
|
||||
T ( 9, "%8u", i ( 1));
|
||||
}
|
||||
|
||||
/* Functions to require optimization to figure out the range of the operand.
|
||||
Used to verify that the checker makes use of the range information to
|
||||
avoid diagnosing the output of sufficiently constrained arguments to
|
||||
integer directives. */
|
||||
|
||||
signed char*
|
||||
range_schar (signed char *val, signed char min, signed char max)
|
||||
{
|
||||
if (*val < min || max < *val) __builtin_abort ();
|
||||
return val;
|
||||
}
|
||||
|
||||
unsigned char*
|
||||
range_uchar (unsigned char *val, unsigned char min, unsigned char max)
|
||||
{
|
||||
if (*val < min || max < *val) __builtin_abort ();
|
||||
return val;
|
||||
}
|
||||
|
||||
signed short*
|
||||
range_sshort (signed short *val, signed short min, signed short max)
|
||||
{
|
||||
if (*val < min || max < *val) __builtin_abort ();
|
||||
return val;
|
||||
}
|
||||
|
||||
unsigned short*
|
||||
range_ushort (unsigned short *val, unsigned short min, unsigned short max)
|
||||
{
|
||||
if (*val < min || max < *val) __builtin_abort ();
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Helper to prevent GCC from figuring out the return value. */
|
||||
extern int idx (void);
|
||||
|
||||
/* Exercise ranges only in types signed and unsigned char and short.
|
||||
No other types work due to bug 71690. */
|
||||
|
||||
void test_sprintf_chk_range_schar (signed char *a)
|
||||
{
|
||||
(void)&a;
|
||||
|
||||
/* Ra creates a range of signed char for A [idx]. A different
|
||||
value is used each time to prevent the ranges from intesecting
|
||||
one another, possibly even eliminating some tests as a result
|
||||
of the range being empty. */
|
||||
#define R(min, max) *range_schar (a + idx (), min, max)
|
||||
|
||||
T ( 0, "%i", R (0, 9)); /* { dg-warning ".%i. directive writing 1 byte into a region of size 0" } */
|
||||
T ( 1, "%i", R (0, 9)); /* { dg-warning "nul past the end" } */
|
||||
T ( 2, "%i", R (0, 9));
|
||||
T ( 2, "%i", R (-1, 0)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
||||
T ( 2, "%i", R (9, 10)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
||||
|
||||
T ( 3, "%i", R ( -9, 9));
|
||||
T ( 3, "%i", R (-99, 99)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
||||
T ( 3, "%i", R ( 0, 99));
|
||||
T ( 3, "%i", R ( 0, 100)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
||||
|
||||
/* The following call may write as few as 3 bytes and as many as 5.
|
||||
It's judgment call how best to diagnose it to make the potential
|
||||
problem clear. */
|
||||
T ( 3, "%i%i", R (1, 10), R (9, 10)); /* { dg-warning ".%i. directive writing between 1 and 2 bytes into a region of size 1" } */
|
||||
|
||||
T ( 4, "%i%i", R (10, 11), R (12, 13)); /* { dg-warning "nul past the end" } */
|
||||
|
||||
T ( 5, "%i%i", R (-9, 99), R (-9, 99));
|
||||
|
||||
T ( 6, "%i_%i_%i", R (0, 9), R (0, 9), R (0, 9));
|
||||
T ( 6, "%i_%i_%i", R (0, 9), R (0, 9), R (0, 10)); /* { dg-warning "may write a terminating nul past the end" } */
|
||||
T ( 6, "%i_%i_%i", R (0, 9), R (0, 10), R (0, 9)); /* { dg-warning "may write a terminating nul past the end" } */
|
||||
T ( 6, "%i_%i_%i", R (0, 10), R (0, 9), R (0, 9)); /* { dg-warning "may write a terminating nul past the end" } */
|
||||
T ( 6, "%i_%i_%i", R (0, 9), R (0, 10), R (0, 10)); /* { dg-warning ".%i. directive writing between 1 and 2 bytes into a region of size 1" } */
|
||||
}
|
||||
|
||||
void test_sprintf_chk_range_uchar (unsigned char *a, unsigned char *b)
|
||||
{
|
||||
(void)&a;
|
||||
(void)&b;
|
||||
|
||||
#undef Ra
|
||||
#define Ra(min, max) *range_uchar (a + idx (), min, max)
|
||||
|
||||
T ( 0, "%i", Ra (0, 9)); /* { dg-warning ".%i. directive writing 1 byte into a region of size 0" } */
|
||||
T ( 1, "%i", Ra (0, 9)); /* { dg-warning "nul past the end" } */
|
||||
T ( 2, "%i", Ra (0, 9));
|
||||
T ( 2, "%i", Ra (9, 10)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
||||
|
||||
T ( 3, "%i", Ra (0, 99));
|
||||
T ( 3, "%i", Ra (0, 100)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
||||
}
|
||||
|
||||
void test_sprintf_chk_range_sshort (signed short *a, signed short *b)
|
||||
{
|
||||
(void)&a;
|
||||
(void)&b;
|
||||
|
||||
#undef Ra
|
||||
#define Ra(min, max) *range_sshort (a + idx (), min, max)
|
||||
|
||||
T ( 0, "%i", Ra ( 0, 9)); /* { dg-warning ".%i. directive writing 1 byte into a region of size 0" } */
|
||||
T ( 1, "%i", Ra ( 0, 1)); /* { dg-warning "nul past the end" } */
|
||||
T ( 1, "%i", Ra ( 0, 9)); /* { dg-warning "nul past the end" } */
|
||||
T ( 2, "%i", Ra ( 0, 1));
|
||||
T ( 2, "%i", Ra ( 8, 9));
|
||||
T ( 2, "%i", Ra ( 0, 9));
|
||||
T ( 2, "%i", Ra (-1, 0)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
||||
T ( 2, "%i", Ra ( 9, 10)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
||||
|
||||
T ( 3, "%i", Ra ( 0, 99));
|
||||
T ( 3, "%i", Ra (99, 999)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
||||
|
||||
T ( 4, "%i", Ra ( 0, 999));
|
||||
T ( 4, "%i", Ra ( 99, 999));
|
||||
T ( 4, "%i", Ra (998, 999));
|
||||
T ( 4, "%i", Ra (999, 1000)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
||||
}
|
33
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-4.c
Normal file
33
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-4.c
Normal file
@ -0,0 +1,33 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-Wformat -Wformat-length=1 -fdiagnostics-show-caret -ftrack-macro-expansion=0" } */
|
||||
|
||||
extern int sprintf (char*, const char*, ...);
|
||||
|
||||
char dst [8];
|
||||
|
||||
void test (void)
|
||||
{
|
||||
sprintf (dst + 7, "%-s", "1");
|
||||
/* { dg-warning "writing a terminating nul past the end of the destination" "" { target *-*-*-* } 10 }
|
||||
{ dg-message "format output 2 bytes into a destination of size 1" "" { target *-*-*-* } 10 }
|
||||
{ dg-begin-multiline-output "" }
|
||||
sprintf (dst + 7, "%-s", "1");
|
||||
^~~~~
|
||||
{ dg-end-multiline-output "" }
|
||||
{ dg-begin-multiline-output "" }
|
||||
sprintf (dst + 7, "%-s", "1");
|
||||
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
sprintf (dst + 7, "%-s", "abcd");
|
||||
/* { dg-warning ".%-s. directive writing 4 bytes into a region of size 1" "" { target *-*-*-* } 22 }
|
||||
{ dg-message "format output 5 bytes into a destination of size 1" "" { target *-*-*-* } 22 }
|
||||
{ dg-begin-multiline-output "" }
|
||||
sprintf (dst + 7, "%-s", "abcd");
|
||||
^~~ ~~~~~~
|
||||
{ dg-end-multiline-output "" }
|
||||
{ dg-begin-multiline-output "" }
|
||||
sprintf (dst + 7, "%-s", "abcd");
|
||||
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
540
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf.c
Normal file
540
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf.c
Normal file
@ -0,0 +1,540 @@
|
||||
/* Test to verify that the return value of calls to __builtin_sprintf
|
||||
that produce a known number of bytes on output is available for
|
||||
constant folding. With optimization enabled the test will fail to
|
||||
link if any of the assertions fails. Without optimization the test
|
||||
aborts at runtime if any of the assertions fails. */
|
||||
/* { dg-do run } */
|
||||
/* { dg-additional-options "-O2 -Wall -Wno-pedantic -fprintf-return-value" } */
|
||||
|
||||
#ifndef LINE
|
||||
# define LINE 0
|
||||
#endif
|
||||
|
||||
#if __STDC_VERSION__ < 199901L
|
||||
# define __func__ __FUNCTION__
|
||||
#endif
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
unsigned ntests;
|
||||
unsigned nfails;
|
||||
|
||||
void __attribute__ ((noclone, noinline))
|
||||
checkv (const char *func, int line, int res, int min, int max,
|
||||
char *dst, const char *fmt, __builtin_va_list va)
|
||||
{
|
||||
int n = __builtin_vsprintf (dst, fmt, va);
|
||||
int len = __builtin_strlen (dst);
|
||||
|
||||
++ntests;
|
||||
|
||||
int fail = 0;
|
||||
if (n != res)
|
||||
{
|
||||
__builtin_printf ("FAIL: %s:%i: \"%s\" expected result for \"%s\" "
|
||||
"doesn't match function call return value: ",
|
||||
func, line, fmt, dst);
|
||||
if (min == max)
|
||||
__builtin_printf ("%i != %i\n", n, min);
|
||||
else
|
||||
__builtin_printf ("%i not in [%i, %i]\n", n, min, max);
|
||||
|
||||
fail = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (len < min || max < len)
|
||||
{
|
||||
__builtin_printf ("FAIL: %s:%i: \"%s\" expected result for \"%s\" "
|
||||
"doesn't match output length: ",
|
||||
func, line, fmt, dst);
|
||||
|
||||
if (min == max)
|
||||
__builtin_printf ("%i != %i\n", len, min);
|
||||
else
|
||||
__builtin_printf ("%i not in [%i, %i]\n", len, min, max);
|
||||
|
||||
fail = 1;
|
||||
}
|
||||
else
|
||||
__builtin_printf ("PASS: %s:%i: \"%s\" result %i: \"%s\"\n",
|
||||
func, line, fmt, n, dst);
|
||||
}
|
||||
|
||||
if (fail)
|
||||
++nfails;
|
||||
}
|
||||
|
||||
void __attribute__ ((noclone, noinline))
|
||||
check (const char *func, int line, int res, int min, int max,
|
||||
char *dst, const char *fmt, ...)
|
||||
{
|
||||
__builtin_va_list va;
|
||||
__builtin_va_start (va, fmt);
|
||||
checkv (func, line, res, min, max, dst, fmt, va);
|
||||
__builtin_va_end (va);
|
||||
}
|
||||
|
||||
char buffer[256];
|
||||
char* volatile dst = buffer;
|
||||
char* ptr = buffer;
|
||||
|
||||
#define concat(a, b) a ## b
|
||||
#define CAT(a, b) concat (a, b)
|
||||
|
||||
#if __OPTIMIZE__
|
||||
/* With optimization references to the following undefined symbol which
|
||||
is unique for each test case are expected to be eliminated. */
|
||||
# define TEST_FAILURE(line, ignore1, ignore2, ignore3) \
|
||||
do { \
|
||||
extern void CAT (failure_on_line_, line)(void); \
|
||||
CAT (failure_on_line_, line)(); \
|
||||
} while (0)
|
||||
#else
|
||||
/* The test is run by DejaGnu with optimization enabled. When it's run
|
||||
with it disabled (i.e., at -O0) each test case is verified at runtime
|
||||
and the test aborts just before exiting if any of them failed. */
|
||||
# define TEST_FAILURE(line, result, min, max) \
|
||||
if (min == max) \
|
||||
__builtin_printf ("FAIL: %s:%i: expected %i, got %i\n", \
|
||||
__func__, line, min, result); \
|
||||
else \
|
||||
__builtin_printf ("FAIL: %s:%i: expected range [%i, %i], got %i\n", \
|
||||
__func__, line, min, max, result);
|
||||
#endif
|
||||
|
||||
/* Verify that the result is exactly equal to RES. */
|
||||
#define EQL(expect, size, fmt, ...) \
|
||||
if (!LINE || LINE == __LINE__) \
|
||||
do { \
|
||||
char *buf = (size) < 0 ? ptr : buffer + sizeof buffer - (size); \
|
||||
int result = __builtin_sprintf (buf, fmt, __VA_ARGS__); \
|
||||
if (result != expect) \
|
||||
{ \
|
||||
TEST_FAILURE (__LINE__, expect, expect, result); \
|
||||
} \
|
||||
check (__func__, __LINE__, result, expect, expect, dst, fmt, \
|
||||
__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
/* Verify that the result is in the range [MIN, MAX]. */
|
||||
#define RNG(min, max, size, fmt, ...) \
|
||||
if (!LINE || LINE == __LINE__) \
|
||||
do { \
|
||||
char *buf = (size) < 0 ? ptr : buffer + sizeof buffer - (size); \
|
||||
int result = __builtin_sprintf (buf, fmt, __VA_ARGS__); \
|
||||
if (result < min || max < result) \
|
||||
{ \
|
||||
TEST_FAILURE (__LINE__, min, max, result); \
|
||||
} \
|
||||
check (__func__, __LINE__, result, min, max, dst, fmt, \
|
||||
__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static void __attribute__ ((noinline, noclone))
|
||||
test_c (char c)
|
||||
{
|
||||
EQL (1, 2, "%c", c);
|
||||
EQL (1, -1, "%c", c);
|
||||
EQL (1, 2, "%1c", c);
|
||||
EQL (1, -1, "%1c", c);
|
||||
EQL (1, 2, "%*c", 1, c);
|
||||
EQL (1, -1, "%*c", 1, c);
|
||||
EQL (2, 3, "%c%c", '1', '2');
|
||||
EQL (2, -1, "%c%c", '1', '2');
|
||||
EQL (3, 4, "%3c", c);
|
||||
EQL (3, -1, "%3c", c);
|
||||
EQL (3, 4, "%*c", 3, c);
|
||||
EQL (3, -1, "%*c", 3, c);
|
||||
|
||||
EQL (3, 4, "%*c%*c", 2, c, 1, c);
|
||||
EQL (3, 4, "%*c%*c", 1, c, 2, c);
|
||||
EQL (3, 4, "%c%c%c", '1', '2', '3');
|
||||
EQL (3, 4, "%*c%c%c", 1, '1', '2', '3');
|
||||
EQL (3, 4, "%*c%*c%c", 1, '1', 1, '2', '3');
|
||||
EQL (3, 4, "%*c%*c%*c", 1, '1', 1, '2', 1, '3');
|
||||
|
||||
EQL (3, -1, "%*c%*c", 2, c, 1, c);
|
||||
EQL (3, -1, "%*c%*c", 1, c, 2, c);
|
||||
EQL (3, -1, "%c%c%c", '1', '2', '3');
|
||||
EQL (3, -1, "%*c%c%c", 1, '1', '2', '3');
|
||||
EQL (3, -1, "%*c%*c%c", 1, '1', 1, '2', '3');
|
||||
EQL (3, -1, "%*c%*c%*c", 1, '1', 1, '2', 1, '3');
|
||||
|
||||
EQL (4, 5, "%c%c %c", '1', '2', '3');
|
||||
EQL (5, 6, "%c %c %c", '1', '2', '3');
|
||||
EQL (5, 6, "%c %c %c", c, c, c);
|
||||
}
|
||||
|
||||
/* Generate a pseudo-random value in the specified range. The return
|
||||
value must be unsigned char to work around limitations in the GCC
|
||||
range information. Similarly for the declaration of rand() whose
|
||||
correct return value should be int, but that also prevents the range
|
||||
information from making it to the printf pass. */
|
||||
|
||||
unsigned char uchar_range (unsigned min, unsigned max)
|
||||
{
|
||||
extern unsigned rand (void);
|
||||
|
||||
unsigned x;
|
||||
x = rand ();
|
||||
|
||||
if (x < min)
|
||||
x = min;
|
||||
else if (max < x)
|
||||
x = max;
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
static void __attribute__ ((noinline, noclone))
|
||||
test_d_i (int i, long li)
|
||||
{
|
||||
/* +-------------------------- expected return value */
|
||||
/* | +---------------------- destination size */
|
||||
/* | | +------------------- format string */
|
||||
/* | | | +-- variable argument(s) */
|
||||
/* | | | | */
|
||||
/* V V V V */
|
||||
EQL ( 1, 2, "%d", 0);
|
||||
EQL ( 2, 3, "%d%d", 0, 1);
|
||||
EQL ( 3, 4, "%d%d", 9, 10);
|
||||
EQL ( 4, 5, "%d%d", 11, 12);
|
||||
EQL ( 5, 6, "%d:%d", 12, 34);
|
||||
EQL ( 5, 6, "%d", 12345);
|
||||
EQL ( 6, 7, "%d", -12345);
|
||||
EQL (15, 16, "%d:%d:%d:%d", 123, 124, 125, 126);
|
||||
|
||||
EQL ( 1, 2, "%i", uchar_range (0, 9));
|
||||
EQL ( 1, -1, "%i", uchar_range (0, 9));
|
||||
|
||||
/* The range information available to passes other than the Value
|
||||
Range Propoagation pass itself is so bad that the following two
|
||||
tests fail (the range seen in the test below is [0, 99] rather
|
||||
than [10, 99].
|
||||
EQL ( 2, 3, "%i", uchar_range (10, 99));
|
||||
EQL ( 3, 4, "%i", uchar_range (100, 199));
|
||||
*/
|
||||
|
||||
/* Verify that the width allows the return value in the following
|
||||
calls can be folded despite the unknown value of the argument. */
|
||||
#if __SIZEOF_INT__ == 2
|
||||
EQL ( 6, 7, "%6d", i);
|
||||
EQL ( 6, 7, "%+6d", i);
|
||||
EQL ( 6, 7, "%-6d", i);
|
||||
EQL ( 6, 7, "%06d", i);
|
||||
#elif __SIZEOF_INT__ == 4
|
||||
EQL (11, 12, "%11d", i);
|
||||
EQL (11, 12, "%+11d", i);
|
||||
EQL (11, 12, "%-11d", i);
|
||||
EQL (11, 12, "%011d", i);
|
||||
#elif __SIZEOF_INT__ == 8
|
||||
EQL (20, 21, "%20d", i);
|
||||
EQL (20, 21, "%+20d", i);
|
||||
EQL (20, 21, "%-29d", i);
|
||||
EQL (20, 21, "%020d", i);
|
||||
#endif
|
||||
|
||||
#if __SIZEOF_LONG__ == 2
|
||||
EQL ( 6, 7, "%6ld", li);
|
||||
EQL ( 6, 7, "%+6ld", li);
|
||||
EQL ( 6, 7, "%-6ld", li);
|
||||
EQL ( 6, 7, "%06ld", li);
|
||||
#elif __SIZEOF_LONG__ == 4
|
||||
EQL (11, 12, "%11ld", li);
|
||||
EQL (11, 12, "%+11ld", li);
|
||||
EQL (11, 12, "%-11ld", li);
|
||||
EQL (11, 12, "%011ld", li);
|
||||
#elif __SIZEOF_LONG__ == 8
|
||||
EQL (20, 21, "%20ld", li);
|
||||
EQL (20, 21, "%+20ld", li);
|
||||
EQL (20, 21, "%-20ld", li);
|
||||
EQL (20, 21, "%020ld", li);
|
||||
#endif
|
||||
|
||||
/* Verify that the output of a directive with an unknown argument
|
||||
is correctly determined at compile time to be in the expected
|
||||
range. */
|
||||
|
||||
/* +---------------------------- expected minimum return value */
|
||||
/* | +------------------------ expected maximum return value */
|
||||
/* | | +-------------------- destination size */
|
||||
/* | | | +----------------- format string */
|
||||
/* | | | | +----- variable argument(s) */
|
||||
/* | | | | | */
|
||||
/* V V V V V */
|
||||
RNG ( 1, 4, 5, "%hhi", i);
|
||||
RNG ( 1, 3, 4, "%hhu", i);
|
||||
|
||||
#if __SIZEOF_SHORT__ == 2
|
||||
RNG ( 1, 6, 7, "%hi", i);
|
||||
RNG ( 1, 5, 6, "%hu", i);
|
||||
#elif __SIZEOF_SHORT__ == 4
|
||||
RNG ( 1, 11, 12, "%hi", i);
|
||||
RNG ( 1, 10, 11, "%hu", i);
|
||||
#endif
|
||||
|
||||
#if __SIZEOF_INT__ == 2
|
||||
RNG ( 1, 6, 7, "%i", i);
|
||||
RNG ( 1, 5, 6, "%u", i);
|
||||
#elif __SIZEOF_INT__ == 4
|
||||
RNG ( 1, 11, 12, "%i", i);
|
||||
RNG ( 1, 10, 11, "%u", i);
|
||||
#elif __SIZEOF_INT__ == 8
|
||||
RNG ( 1, 20, 21, "%i", i);
|
||||
RNG ( 1, 19, 20, "%u", i);
|
||||
#endif
|
||||
|
||||
#if __SIZEOF_LONG__ == 4
|
||||
RNG ( 1, 11, 12, "%li", li);
|
||||
RNG ( 1, 10, 11, "%lu", li);
|
||||
#elif __SIZEOF_LONG__ == 8
|
||||
RNG ( 1, 20, 21, "%li", li);
|
||||
RNG ( 1, 19, 20, "%lu", li);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __attribute__ ((noinline, noclone))
|
||||
test_x (unsigned char uc, unsigned short us, unsigned ui)
|
||||
{
|
||||
EQL ( 1, 2, "%hhx", 0);
|
||||
EQL ( 2, 3, "%2hhx", 0);
|
||||
EQL ( 2, 3, "%02hhx", 0);
|
||||
EQL ( 2, 3, "%#02hhx", 0);
|
||||
|
||||
EQL ( 1, 2, "%hhx", 1);
|
||||
EQL ( 2, 3, "%2hhx", 1);
|
||||
EQL ( 2, 3, "%02hhx", 1);
|
||||
EQL ( 3, 4, "%#02hhx", 1);
|
||||
|
||||
EQL ( 2, 3, "%2hhx", uc);
|
||||
EQL ( 2, 3, "%02hhx", uc);
|
||||
EQL ( 5, 6, "%#05hhx", uc);
|
||||
|
||||
EQL ( 2, 3, "%2hhx", us);
|
||||
EQL ( 2, 3, "%02hhx", us);
|
||||
EQL ( 5, 6, "%#05hhx", us);
|
||||
|
||||
EQL ( 2, 3, "%2hhx", ui);
|
||||
EQL ( 2, 3, "%02hhx", ui);
|
||||
EQL ( 5, 6, "%#05hhx", ui);
|
||||
|
||||
EQL ( 1, 2, "%x", 0);
|
||||
EQL ( 1, 2, "%#x", 0);
|
||||
EQL ( 1, 2, "%#0x", 0);
|
||||
EQL ( 1, 2, "%x", 1);
|
||||
EQL ( 1, 2, "%x", 0xf);
|
||||
EQL ( 2, 3, "%x", 0x10);
|
||||
EQL ( 2, 3, "%x", 0xff);
|
||||
EQL ( 3, 4, "%x", 0x100);
|
||||
|
||||
EQL (11, 12, "%02x:%02x:%02x:%02x", 0xde, 0xad, 0xbe, 0xef);
|
||||
|
||||
/* The following would be optimized if the range information of
|
||||
the variable's type was made available. Alas, it's lost due
|
||||
to the promotion of the actual argument (unsined char) to
|
||||
the type of the "formal" argument (int in the case of the
|
||||
ellipsis).
|
||||
EQL (11, 12, "%02x:%02x:%02x:%02x", uc, uc, uc, uc);
|
||||
*/
|
||||
EQL (11, 12, "%02hhx:%02hhx:%02hhx:%02hhx", uc, uc, uc, uc);
|
||||
|
||||
#if __SIZEOF_SHORT__ == 2
|
||||
EQL ( 4, 5, "%04hx", us);
|
||||
EQL ( 9, 10, "%04hx:%04hx", us, us);
|
||||
EQL (14, 15, "%04hx:%04hx:%04hx", us, us, us);
|
||||
EQL (19, 20, "%04hx:%04hx:%04hx:%04hx", us, us, us, us);
|
||||
#endif
|
||||
|
||||
#if __SIZEOF_INT__ == 2
|
||||
EQL ( 4, 5, "%04x", ui);
|
||||
EQL ( 6, 7, "%#06x", ui);
|
||||
#elif __SIZEOF_INT__ == 4
|
||||
EQL ( 8, 9, "%08x", ui);
|
||||
EQL (10, 10 + 1, "%#010x", ui);
|
||||
#elif __SIZEOF_INT__ == 8
|
||||
EQL (16, 17, "%016x", ui);
|
||||
EQL (18, 19, "%#018x", ui);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __attribute__ ((noinline, noclone))
|
||||
test_a_double (void)
|
||||
{
|
||||
EQL ( 6, 7, "%a", 0.0); /* 0x0p+0 */
|
||||
EQL ( 6, 7, "%a", 1.0); /* 0x8p-3 */
|
||||
EQL ( 6, 7, "%a", 2.0); /* 0x8p-2 */
|
||||
|
||||
EQL ( 8, 9, "%.1a", 3.0); /* 0xc.0p-2 */
|
||||
EQL ( 9, 10, "%.2a", 4.0); /* 0xa.00p-1 */
|
||||
}
|
||||
|
||||
static void __attribute__ ((noinline, noclone))
|
||||
test_a_long_double (void)
|
||||
{
|
||||
EQL ( 6, 7, "%La", 0.0L); /* 0x0p+0 */
|
||||
EQL ( 6, 7, "%La", 1.0L); /* 0x8p-3 */
|
||||
EQL ( 6, 7, "%La", 2.0L); /* 0x8p-2 */
|
||||
|
||||
EQL ( 8, 9, "%.1La", 3.0L); /* 0xc.0p-2 */
|
||||
EQL ( 9, 10, "%.2La", 4.0L); /* 0xa.00p-1 */
|
||||
}
|
||||
|
||||
static void __attribute__ ((noinline, noclone))
|
||||
test_e_double (void)
|
||||
{
|
||||
EQL (12, 13, "%e", 1.0e0);
|
||||
EQL (13, 14, "%e", -1.0e0);
|
||||
EQL (12, 13, "%e", 1.0e+1);
|
||||
EQL (13, 14, "%e", -1.0e+1);
|
||||
EQL (12, 13, "%e", 1.0e+12);
|
||||
EQL (13, 14, "%e", -1.0e+12);
|
||||
EQL (13, 14, "%e", 1.0e+123);
|
||||
EQL (14, 15, "%e", -1.0e+123);
|
||||
|
||||
EQL (12, 13, "%e", 9.999e+99);
|
||||
EQL (12, 13, "%e", 9.9999e+99);
|
||||
EQL (12, 13, "%e", 9.99999e+99);
|
||||
|
||||
/* The actual output of the following directive depends on the rounding
|
||||
mode. */
|
||||
/* EQL (12, "%e", 9.9999994e+99); */
|
||||
|
||||
EQL (12, 13, "%e", 1.0e-1);
|
||||
EQL (12, 13, "%e", 1.0e-12);
|
||||
EQL (13, 14, "%e", 1.0e-123);
|
||||
}
|
||||
|
||||
static void __attribute__ ((noinline, noclone))
|
||||
test_e_long_double (void)
|
||||
{
|
||||
EQL (12, 13, "%Le", 1.0e0L);
|
||||
EQL (13, 14, "%Le", -1.0e0L);
|
||||
EQL (12, 13, "%Le", 1.0e+1L);
|
||||
EQL (13, 14, "%Le", -1.0e+1L);
|
||||
EQL (12, 13, "%Le", 1.0e+12L);
|
||||
EQL (13, 14, "%Le", -1.0e+12L);
|
||||
EQL (13, 14, "%Le", 1.0e+123L);
|
||||
EQL (14, 15, "%Le", -1.0e+123L);
|
||||
|
||||
EQL (12, 13, "%Le", 9.999e+99L);
|
||||
EQL (12, 13, "%Le", 9.9999e+99L);
|
||||
EQL (12, 13, "%Le", 9.99999e+99L);
|
||||
EQL (12, 13, "%Le", 9.999999e+99L);
|
||||
|
||||
/* The actual output of the following directive depends on the rounding
|
||||
mode. */
|
||||
/* EQL (12, "%Le", 9.9999994e+99L); */
|
||||
|
||||
EQL (12, 13, "%Le", 1.0e-1L);
|
||||
EQL (12, 13, "%Le", 1.0e-12L);
|
||||
EQL (13, 14, "%Le", 1.0e-123L);
|
||||
|
||||
EQL ( 6, 7, "%.0Le", 1.0e-111L);
|
||||
EQL ( 8, 9, "%.1Le", 1.0e-111L);
|
||||
EQL (19, 20, "%.12Le", 1.0e-112L);
|
||||
EQL (20, 21, "%.13Le", 1.0e-113L);
|
||||
}
|
||||
|
||||
static void __attribute__ ((noinline, noclone))
|
||||
test_f_double (void)
|
||||
{
|
||||
EQL ( 8, 9, "%f", 0.0e0);
|
||||
EQL ( 8, 9, "%f", 0.1e0);
|
||||
EQL ( 8, 9, "%f", 0.12e0);
|
||||
EQL ( 8, 9, "%f", 0.123e0);
|
||||
EQL ( 8, 9, "%f", 0.1234e0);
|
||||
EQL ( 8, 9, "%f", 0.12345e0);
|
||||
EQL ( 8, 9, "%f", 0.123456e0);
|
||||
EQL ( 8, 9, "%f", 1.234567e0);
|
||||
|
||||
EQL ( 9, 10, "%f", 1.0e+1);
|
||||
EQL ( 20, 21, "%f", 1.0e+12);
|
||||
EQL (130, 131, "%f", 1.0e+123);
|
||||
|
||||
EQL ( 8, 9, "%f", 1.0e-1);
|
||||
EQL ( 8, 9, "%f", 1.0e-12);
|
||||
EQL ( 8, 9, "%f", 1.0e-123);
|
||||
}
|
||||
|
||||
static void __attribute__ ((noinline, noclone))
|
||||
test_f_long_double (void)
|
||||
{
|
||||
EQL ( 8, 9, "%Lf", 0.0e0L);
|
||||
EQL ( 8, 9, "%Lf", 0.1e0L);
|
||||
EQL ( 8, 9, "%Lf", 0.12e0L);
|
||||
EQL ( 8, 9, "%Lf", 0.123e0L);
|
||||
EQL ( 8, 9, "%Lf", 0.1234e0L);
|
||||
EQL ( 8, 9, "%Lf", 0.12345e0L);
|
||||
EQL ( 8, 9, "%Lf", 0.123456e0L);
|
||||
EQL ( 8, 9, "%Lf", 1.234567e0L);
|
||||
|
||||
EQL ( 9, 10, "%Lf", 1.0e+1L);
|
||||
EQL ( 20, 21, "%Lf", 1.0e+12L);
|
||||
EQL (130, 131, "%Lf", 1.0e+123L);
|
||||
|
||||
EQL ( 8, 9, "%Lf", 1.0e-1L);
|
||||
EQL ( 8, 9, "%Lf", 1.0e-12L);
|
||||
EQL ( 8, 9, "%Lf", 1.0e-123L);
|
||||
}
|
||||
|
||||
static void __attribute__ ((noinline, noclone))
|
||||
test_s (int i)
|
||||
{
|
||||
EQL ( 0, 1, "%s", "");
|
||||
EQL ( 0, 1, "%s", "\0");
|
||||
EQL ( 1, 2, "%1s", "");
|
||||
EQL ( 1, 2, "%s", "1");
|
||||
EQL ( 2, 3, "%2s", "");
|
||||
EQL ( 2, 3, "%s", "12");
|
||||
EQL ( 2, 3, "%s%s", "12", "");
|
||||
EQL ( 2, 3, "%s%s", "", "12");
|
||||
EQL ( 2, 3, "%s%s", "1", "2");
|
||||
EQL ( 3, 4, "%3s", "");
|
||||
EQL ( 3, 4, "%3s", "1");
|
||||
EQL ( 3, 4, "%3s", "12");
|
||||
EQL ( 3, 4, "%3s", "123");
|
||||
EQL ( 3, 4, "%3.3s", "1");
|
||||
EQL ( 3, 4, "%3.3s", "12");
|
||||
EQL ( 3, 4, "%3.3s", "123");
|
||||
EQL ( 3, 4, "%3.3s", "1234");
|
||||
EQL ( 3, 4, "%3.3s", "12345");
|
||||
EQL ( 3, 4, "%s %s", "1", "2");
|
||||
EQL ( 4, 5, "%s %s", "12", "3");
|
||||
EQL ( 5, 6, "%s %s", "12", "34");
|
||||
EQL ( 5, 6, "[%s %s]", "1", "2");
|
||||
EQL ( 6, 7, "[%s %s]", "12", "3");
|
||||
EQL ( 7, 8, "[%s %s]", "12", "34");
|
||||
|
||||
/* Verify the result of a conditional expression involving string
|
||||
literals is in the expected range of their lengths. */
|
||||
RNG ( 0, 3, 4, "%-s", i ? "" : "123");
|
||||
RNG ( 1, 4, 5, "%-s", i ? "1" : "1234");
|
||||
RNG ( 2, 5, 6, "%-s", i ? "12" : "12345");
|
||||
RNG ( 3, 6, 7, "%-s", i ? "123" : "123456");
|
||||
}
|
||||
|
||||
int main (void)
|
||||
{
|
||||
test_c ('?');
|
||||
test_d_i (0xdeadbeef, 0xdeadbeefL);
|
||||
test_x ('?', 0xdead, 0xdeadbeef);
|
||||
|
||||
test_a_double ();
|
||||
test_e_double ();
|
||||
test_f_double ();
|
||||
|
||||
test_a_long_double ();
|
||||
test_e_long_double ();
|
||||
test_f_long_double ();
|
||||
|
||||
test_s (0);
|
||||
|
||||
if (nfails)
|
||||
{
|
||||
__builtin_printf ("%u out of %u tests failed\n", nfails, ntests);
|
||||
__builtin_abort ();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -471,6 +471,7 @@ extern simple_ipa_opt_pass *make_pass_ipa_oacc (gcc::context *ctxt);
|
||||
extern simple_ipa_opt_pass *make_pass_ipa_oacc_kernels (gcc::context *ctxt);
|
||||
extern gimple_opt_pass *make_pass_gen_hsail (gcc::context *ctxt);
|
||||
extern gimple_opt_pass *make_pass_warn_nonnull_compare (gcc::context *ctxt);
|
||||
extern gimple_opt_pass *make_pass_sprintf_length (gcc::context *ctxt);
|
||||
|
||||
/* IPA Passes */
|
||||
extern simple_ipa_opt_pass *make_pass_ipa_lower_emutls (gcc::context *ctxt);
|
||||
|
Loading…
Reference in New Issue
Block a user