diff --git a/gcc/doc/gty.texi b/gcc/doc/gty.texi index 81aafd11ce3e32a7386704b7ea7f1a2437833632..4f791b300ba44c82b88686edee33bffeac946f9e 100644 --- a/gcc/doc/gty.texi +++ b/gcc/doc/gty.texi @@ -196,7 +196,26 @@ static GTY((length("reg_known_value_size"))) rtx *reg_known_value; Note that the @code{length} option is only meant for use with arrays of non-atomic objects, that is, objects that contain pointers pointing to other GTY-managed objects. For other GC-allocated arrays and strings -you should use @code{atomic}. +you should use @code{atomic} or @code{string_length}. + +@findex string_length +@item string_length ("@var{expression}") + +In order to simplify production of PCH, a structure member that is a plain +array of bytes (an optionally @code{const} and/or @code{unsigned} @code{char +*}) is treated specially by the infrastructure. Even if such an array has not +been allocated in GC-controlled memory, it will still be written properly into +a PCH. The machinery responsible for this needs to know the length of the +data; by default, the length is determined by calling @code{strlen} on the +pointer. The @code{string_length} option specifies an alternate way to +determine the length, such as by inspecting another struct member: + +@smallexample +struct GTY(()) non_terminated_string @{ + size_t sz; + const char * GTY((string_length ("%h.sz"))) data; +@}; +@end smallexample @findex skip @item skip diff --git a/gcc/gengtype.cc b/gcc/gengtype.cc index 42363439bd8bd73ad5215b12145d1b44eedc9cca..28bf05e9c57fa6afbc19992f8de66717b1f96d88 100644 --- a/gcc/gengtype.cc +++ b/gcc/gengtype.cc @@ -2403,7 +2403,7 @@ struct write_types_data enum write_types_kinds kind; }; -static void output_escaped_param (struct walk_type_data *d, +static void output_escaped_param (const struct walk_type_data *d, const char *, const char *); static void output_mangled_typename (outf_p, const_type_p); static void walk_type (type_p t, struct walk_type_data *d); @@ -2537,7 +2537,7 @@ output_mangled_typename (outf_p of, const_type_p t) print error messages. */ static void -output_escaped_param (struct walk_type_data *d, const char *param, +output_escaped_param (const struct walk_type_data *d, const char *param, const char *oname) { const char *p; @@ -2576,7 +2576,7 @@ const char * get_string_option (options_p opt, const char *key) { for (; opt; opt = opt->next) - if (strcmp (opt->name, key) == 0) + if (opt->kind == OPTION_STRING && strcmp (opt->name, key) == 0) return opt->info.string; return NULL; } @@ -2700,6 +2700,8 @@ walk_type (type_p t, struct walk_type_data *d) ; else if (strcmp (oo->name, "callback") == 0) ; + else if (strcmp (oo->name, "string_length") == 0) + ; else error_at_line (d->line, "unknown option `%s'\n", oo->name); @@ -3251,7 +3253,22 @@ write_types_process_field (type_p f, const struct walk_type_data *d) { oprintf (d->of, "%*sgt_%s_", d->indent, "", wtd->prefix); output_mangled_typename (d->of, f); - oprintf (d->of, " (%s%s);\n", cast, d->val); + + /* Check if we need to call the special pch note version + for strings that takes an explicit length. */ + const auto length_override + = (f->kind == TYPE_STRING && !strcmp (wtd->prefix, "pch_n") + ? get_string_option (d->opt, "string_length") + : nullptr); + if (length_override) + { + oprintf (d->of, "2 (%s%s, ", cast, d->val); + output_escaped_param (d, length_override, "string_length"); + } + else + oprintf (d->of, " (%s%s", cast, d->val); + + oprintf (d->of, ");\n"); if (d->reorder_fn && wtd->reorder_note_routine) oprintf (d->of, "%*s%s (%s%s, %s%s, %s);\n", d->indent, "", wtd->reorder_note_routine, cast, d->val, cast, d->val, diff --git a/gcc/ggc-common.cc b/gcc/ggc-common.cc index 8b3389e8760e9c32ed4032e623b03a9fec85c376..62da09d66a7372c6a79c0d4005f7a5740b1f2eb6 100644 --- a/gcc/ggc-common.cc +++ b/gcc/ggc-common.cc @@ -253,7 +253,8 @@ static vec<void *> reloc_addrs_vec; int gt_pch_note_object (void *obj, void *note_ptr_cookie, - gt_note_pointers note_ptr_fn) + gt_note_pointers note_ptr_fn, + size_t length_override) { struct ptr_data **slot; @@ -273,7 +274,9 @@ gt_pch_note_object (void *obj, void *note_ptr_cookie, (*slot)->obj = obj; (*slot)->note_ptr_fn = note_ptr_fn; (*slot)->note_ptr_cookie = note_ptr_cookie; - if (note_ptr_fn == gt_pch_p_S) + if (length_override != (size_t)-1) + (*slot)->size = length_override; + else if (note_ptr_fn == gt_pch_p_S) (*slot)->size = strlen ((const char *)obj) + 1; else (*slot)->size = ggc_get_size (obj); diff --git a/gcc/ggc.h b/gcc/ggc.h index aeec1bafb9b3fbb24dc1f7d9e55fb2efa3d5b466..7bc74ec82b530f3d8ffca6fd81aa7f38d151abdf 100644 --- a/gcc/ggc.h +++ b/gcc/ggc.h @@ -44,7 +44,8 @@ typedef void (*gt_handle_reorder) (void *, void *, gt_pointer_operator, void *); /* Used by the gt_pch_n_* routines. Register an object in the hash table. */ -extern int gt_pch_note_object (void *, void *, gt_note_pointers); +extern int gt_pch_note_object (void *, void *, gt_note_pointers, + size_t length_override = (size_t)-1); /* Used by the gt_pch_p_* routines. Register address of a callback pointer. */ @@ -101,6 +102,7 @@ extern int ggc_marked_p (const void *); /* PCH and GGC handling for strings, mostly trivial. */ extern void gt_pch_n_S (const void *); +extern void gt_pch_n_S2 (const void *, size_t); extern void gt_ggc_m_S (const void *); /* End of GTY machinery API. */ diff --git a/gcc/stringpool.cc b/gcc/stringpool.cc index 57509d58e15ccff6b3a64a7a732ab4af031dea7e..20dbef5580c79558b1e386962d0e9a550f0214bb 100644 --- a/gcc/stringpool.cc +++ b/gcc/stringpool.cc @@ -196,6 +196,13 @@ gt_pch_n_S (const void *x) >_pch_p_S); } +void +gt_pch_n_S2 (const void *x, size_t string_len) +{ + gt_pch_note_object (CONST_CAST (void *, x), CONST_CAST (void *, x), + >_pch_p_S, string_len); +} + /* User-callable entry point for marking string X. */ diff --git a/gcc/testsuite/g++.dg/pch/pch-string-nulls.C b/gcc/testsuite/g++.dg/pch/pch-string-nulls.C new file mode 100644 index 0000000000000000000000000000000000000000..dfeb21adf7173fe03971a97d049ff58de757cedc --- /dev/null +++ b/gcc/testsuite/g++.dg/pch/pch-string-nulls.C @@ -0,0 +1,3 @@ +// { dg-do compile { target c++11 } } +#include "pch-string-nulls.H" +static_assert (X[4] == '[' && X[5] == '!' && X[6] == ']', "error"); diff --git a/gcc/testsuite/g++.dg/pch/pch-string-nulls.Hs b/gcc/testsuite/g++.dg/pch/pch-string-nulls.Hs new file mode 100644 index 0000000000000000000000000000000000000000..02f43174eef43b5efcab457021fe9938ce546da4 Binary files /dev/null and b/gcc/testsuite/g++.dg/pch/pch-string-nulls.Hs differ diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h index d5ef12a30ead7cf13045cee0b911f94e9c62ec26..1d34c00669fca653c11c094cb6af6569a9945dd7 100644 --- a/libcpp/include/cpplib.h +++ b/libcpp/include/cpplib.h @@ -179,7 +179,11 @@ enum c_lang {CLK_GNUC89 = 0, CLK_GNUC99, CLK_GNUC11, CLK_GNUC17, CLK_GNUC2X, /* Payload of a NUMBER, STRING, CHAR or COMMENT token. */ struct GTY(()) cpp_string { unsigned int len; - const unsigned char *text; + + /* TEXT is always null terminated (terminator not included in len); but this + GTY markup arranges that PCH streaming works properly even if there is a + null byte in the middle of the string. */ + const unsigned char * GTY((string_length ("1 + %h.len"))) text; }; /* Flags for the cpp_token structure. */ diff --git a/libcpp/include/symtab.h b/libcpp/include/symtab.h index 53efe6c3943aee70352a93ce2de3b931565f2345..8b45fd5c2ceb94d18c6547a97935991f951cade6 100644 --- a/libcpp/include/symtab.h +++ b/libcpp/include/symtab.h @@ -29,7 +29,10 @@ along with this program; see the file COPYING3. If not see typedef struct ht_identifier ht_identifier; typedef struct ht_identifier *ht_identifier_ptr; struct GTY(()) ht_identifier { - const unsigned char *str; + /* This GTY markup arranges that the null-terminated identifier would still + stream to PCH correctly, if a null byte were to make its way into an + identifier somehow. */ + const unsigned char * GTY((string_length ("1 + %h.len"))) str; unsigned int len; unsigned int hash_value; };