[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <D6E2A1BD-5D42-4492-B190-A2B845D8B0EE@oracle.com>
Date: Wed, 17 Sep 2025 17:56:17 +0000
From: Qing Zhao <qing.zhao@...cle.com>
To: Kees Cook <kees@...nel.org>
CC: Andrew Pinski <pinskia@...il.com>, Jakub Jelinek <jakub@...hat.com>,
Martin Uecker <uecker@...raz.at>, Richard Biener <rguenther@...e.de>,
Joseph
Myers <josmyers@...hat.com>,
Peter Zijlstra <peterz@...radead.org>, Jan
Hubicka <hubicka@....cz>,
Richard Earnshaw <richard.earnshaw@....com>,
Richard Sandiford <richard.sandiford@....com>,
Marcus Shawcroft
<marcus.shawcroft@....com>,
Kyrylo Tkachov <kyrylo.tkachov@....com>,
Kito
Cheng <kito.cheng@...il.com>,
Palmer Dabbelt <palmer@...belt.com>,
Andrew
Waterman <andrew@...ive.com>,
Jim Wilson <jim.wilson.gcc@...il.com>,
Dan Li
<ashimida.1990@...il.com>,
Sami Tolvanen <samitolvanen@...gle.com>,
Ramon de
C Valle <rcvalle@...gle.com>,
Joao Moreira <joao@...rdrivepizza.com>,
Nathan
Chancellor <nathan@...nel.org>,
Bill Wendling <morbo@...gle.com>,
"gcc-patches@....gnu.org" <gcc-patches@....gnu.org>,
"linux-hardening@...r.kernel.org" <linux-hardening@...r.kernel.org>
Subject: Re: [PATCH v3 1/7] typeinfo: Introduce KCFI typeinfo mangling API
Hi,
> On Sep 13, 2025, at 19:23, Kees Cook <kees@...nel.org> wrote:
>
> To support the KCFI typeid and future type-based allocators,
Could you please explain a little bit more on the “future type-based allocators”?
And why these two new builtins are necessary for this purpose?
> which need
> to convert unique types into unique 32-bit values, add a mangling system
> based on the Itanium C++ mangling ABI, adapted for for C types.
There is a redundant “for” in the above last sentence.
> Introduce
> __builtin_typeinfo_hash for the hash, and __builtin_typeinfo_name for
> testing and debugging (to see the human-readable mangling form).
In addition to the testing and debugging purpose, are there any use cases for
these two new compiler provided builtins?
thanks.
Qing
> Add
> tests for typeinfo validation and error handling.
>
> gcc/ChangeLog:
>
> * Makefile.in: Add kcfi-typeinfo.o.
> * doc/extend.texi: Document typeinfo builtins.
> * kcfi-typeinfo.h: New file, typeinfo mangling API.
> * kcfi-typeinfo.cc: New file, implement typeinfo mangling.
>
> gcc/c-family/ChangeLog:
>
> * c-common.h (enum rid): Add typeinfo builtins.
> * c-common.cc: Add typeinfo builtins.
>
> gcc/c/ChangeLog:
>
> * c-parser.cc (c_parser_get_builtin_type_arg): New function,
> parse type.
> (c_parser_postfix_expression): Add typeinfo builtins.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.dg/builtin-typeinfo-errors.c: New test, validate bad
> arguments are rejected.
> * gcc.dg/builtin-typeinfo.c: New test, typeinfo mangling.
>
> Signed-off-by: Kees Cook <kees@...nel.org>
> ---
> gcc/Makefile.in | 1 +
> gcc/c-family/c-common.h | 1 +
> gcc/kcfi-typeinfo.h | 32 ++
> .../gcc.dg/builtin-typeinfo-errors.c | 28 ++
> gcc/testsuite/gcc.dg/builtin-typeinfo.c | 350 +++++++++++++
> gcc/c-family/c-common.cc | 2 +
> gcc/c/c-parser.cc | 72 +++
> gcc/doc/extend.texi | 94 ++++
> gcc/kcfi-typeinfo.cc | 475 ++++++++++++++++++
> 9 files changed, 1055 insertions(+)
> create mode 100644 gcc/kcfi-typeinfo.h
> create mode 100644 gcc/testsuite/gcc.dg/builtin-typeinfo-errors.c
> create mode 100644 gcc/testsuite/gcc.dg/builtin-typeinfo.c
> create mode 100644 gcc/kcfi-typeinfo.cc
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index d2744db843d7..a14fb498ce44 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1591,6 +1591,7 @@ OBJS = \
> ira-emit.o \
> ira-lives.o \
> jump.o \
> + kcfi-typeinfo.o \
> langhooks.o \
> late-combine.o \
> lcm.o \
> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> index b6021d241731..e0100837946e 100644
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -112,6 +112,7 @@ enum rid
> RID_BUILTIN_SHUFFLEVECTOR, RID_BUILTIN_CONVERTVECTOR, RID_BUILTIN_TGMATH,
> RID_BUILTIN_HAS_ATTRIBUTE, RID_BUILTIN_ASSOC_BARRIER, RID_BUILTIN_STDC,
> RID_BUILTIN_COUNTED_BY_REF,
> + RID_BUILTIN_TYPEINFO_NAME, RID_BUILTIN_TYPEINFO_HASH,
> RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128, RID_DFLOAT64X,
>
> /* TS 18661-3 keywords, in the same sequence as the TI_* values. */
> diff --git a/gcc/kcfi-typeinfo.h b/gcc/kcfi-typeinfo.h
> new file mode 100644
> index 000000000000..805f9ebaeca4
> --- /dev/null
> +++ b/gcc/kcfi-typeinfo.h
> @@ -0,0 +1,32 @@
> +/* KCFI-compatible type mangling, based on Itanium C++ ABI.
> + Copyright (C) 2025 Free Software Foundation, Inc.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it under
> +the terms of the GNU General Public License as published by the Free
> +Software Foundation; either version 3, or (at your option) any later
> +version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY
> +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3. If not see
> +<http://www.gnu.org/licenses/>. */
> +
> +#ifndef GCC_KCFI_TYPEINFO_H
> +#define GCC_KCFI_TYPEINFO_H
> +
> +#include "tree.h"
> +#include <string>
> +
> +/* Get the typeinfo mangled name string for any C type. */
> +extern std::string typeinfo_get_name (tree type);
> +
> +/* Get the typeinfo hash for any C type. */
> +extern uint32_t typeinfo_get_hash (tree type);
> +
> +#endif /* GCC_KCFI_TYPEINFO_H */
> diff --git a/gcc/testsuite/gcc.dg/builtin-typeinfo-errors.c b/gcc/testsuite/gcc.dg/builtin-typeinfo-errors.c
> new file mode 100644
> index 000000000000..71ad01337b4e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-typeinfo-errors.c
> @@ -0,0 +1,28 @@
> +/* Test error handling for __builtin_typeinfo_name and __builtin_typeinfo_hash. */
> +/* { dg-do compile } */
> +
> +int main() {
> + /* Test missing arguments */
> + const char *result1 = __builtin_typeinfo_name(); /* { dg-error "expected specifier-qualifier-list before '\\)'" } */
> + /* { dg-error "expected type name in '__builtin_typeinfo_name'" "" { target *-*-* } .-1 } */
> + unsigned int result2 = __builtin_typeinfo_hash(); /* { dg-error "expected specifier-qualifier-list before '\\)'" } */
> + /* { dg-error "expected type name in '__builtin_typeinfo_hash'" "" { target *-*-* } .-1 } */
> +
> + /* Test wrong argument types (expressions instead of type names) */
> + const char *result3 = __builtin_typeinfo_name(42); /* { dg-error "expected specifier-qualifier-list before numeric constant" } */
> + /* { dg-error "expected type name in '__builtin_typeinfo_name'" "" { target *-*-* } .-1 } */
> + unsigned int result4 = __builtin_typeinfo_hash(42); /* { dg-error "expected specifier-qualifier-list before numeric constant" } */
> + /* { dg-error "expected type name in '__builtin_typeinfo_hash'" "" { target *-*-* } .-1 } */
> +
> + int x = 5;
> + const char *result5 = __builtin_typeinfo_name(x); /* { dg-error "expected specifier-qualifier-list before" } */
> + /* { dg-error "expected type name in '__builtin_typeinfo_name'" "" { target *-*-* } .-1 } */
> + unsigned int result6 = __builtin_typeinfo_hash(x); /* { dg-error "expected specifier-qualifier-list before" } */
> + /* { dg-error "expected type name in '__builtin_typeinfo_hash'" "" { target *-*-* } .-1 } */
> +
> + /* Test too many arguments */
> + const char *result7 = __builtin_typeinfo_name(int, int); /* { dg-error "expected '\\)' before ','" } */
> + unsigned int result8 = __builtin_typeinfo_hash(int, int); /* { dg-error "expected '\\)' before ','" } */
> +
> + return 0;
> +}
> diff --git a/gcc/testsuite/gcc.dg/builtin-typeinfo.c b/gcc/testsuite/gcc.dg/builtin-typeinfo.c
> new file mode 100644
> index 000000000000..744dc50f407e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-typeinfo.c
> @@ -0,0 +1,350 @@
> +/* Test KCFI type mangling using __builtin_typeinfo_name. */
> +/* { dg-do run } */
> +/* { dg-options "-std=gnu99" } */
> +
> +#include <stdio.h>
> +#include <string.h>
> +#include <stdarg.h>
> +
> +int pass, fail;
> +
> +#define TEST_STRING(expr, expected_string) \
> + do { \
> + const char *actual_string = __builtin_typeinfo_name(typeof(expr)); \
> + printf("Testing %s: ", #expr); \
> + if (strcmp(actual_string, expected_string) == 0) { \
> + printf("PASS (%s)\n", actual_string); \
> + pass ++; \
> + } else { \
> + printf("FAIL\n"); \
> + printf(" Expected: %s\n", expected_string); \
> + printf(" Actual: %s\n", actual_string); \
> + fail ++; \
> + } \
> + } while (0)
> +
> +int main(void)
> +{
> + printf("Testing KCFI Typeinfo Mangling\n");
> + printf("======================================================\n");
> +
> + /* Test basic types */
> + TEST_STRING(void, "_ZTSv");
> + TEST_STRING(char, "_ZTSc");
> + TEST_STRING(int, "_ZTSi");
> + TEST_STRING(short, "_ZTSs");
> + TEST_STRING(long, "_ZTSl");
> + TEST_STRING(float, "_ZTSf");
> + TEST_STRING(double, "_ZTSd");
> +
> + /* Test qualified types */
> + TEST_STRING(const int, "_ZTSKi");
> + TEST_STRING(volatile int, "_ZTSVi");
> +
> + /* Test pointer types */
> + TEST_STRING(char*, "_ZTSPc");
> + TEST_STRING(int*, "_ZTSPi");
> + TEST_STRING(void*, "_ZTSPv");
> + TEST_STRING(const char*, "_ZTSPKc");
> +
> + /* Test array types */
> + TEST_STRING(int[10], "_ZTSA10_i");
> + TEST_STRING(char[20], "_ZTSA20_c");
> + TEST_STRING(short[], "_ZTSA_s");
> +
> + /* Test basic function types */
> + extern void func_void(void);
> + extern void func_char(char x);
> + extern void func_short(short x);
> + extern void func_int(int x);
> + extern void func_long(long x);
> + TEST_STRING(func_void, "_ZTSFvvE");
> + TEST_STRING(func_char, "_ZTSFvcE");
> + TEST_STRING(func_short, "_ZTSFvsE");
> + TEST_STRING(func_int, "_ZTSFviE");
> + TEST_STRING(func_long, "_ZTSFvlE");
> +
> + /* Test functions with unsigned types */
> + extern void func_unsigned_char(unsigned char x);
> + extern void func_unsigned_short(unsigned short x);
> + extern void func_unsigned_int(unsigned int x);
> + TEST_STRING(func_unsigned_char, "_ZTSFvhE");
> + TEST_STRING(func_unsigned_short, "_ZTSFvtE");
> + TEST_STRING(func_unsigned_int, "_ZTSFvjE");
> +
> + /* Test functions with signed types */
> + extern void func_signed_char(signed char x);
> + extern void func_signed_short(signed short x);
> + extern void func_signed_int(signed int x);
> + TEST_STRING(func_signed_char, "_ZTSFvaE");
> + TEST_STRING(func_signed_short, "_ZTSFvsE");
> + TEST_STRING(func_signed_int, "_ZTSFviE");
> +
> + /* Test functions with pointer types */
> + extern void func_void_ptr(void *x);
> + extern void func_char_ptr(char *x);
> + extern void func_short_ptr(short *x);
> + extern void func_int_ptr(int *x);
> + extern void func_int_array(int arr[]); /* Decays to "int *". */
> + extern void func_long_ptr(long *x);
> + TEST_STRING(func_void_ptr, "_ZTSFvPvE");
> + TEST_STRING(func_char_ptr, "_ZTSFvPcE");
> + TEST_STRING(func_short_ptr, "_ZTSFvPsE");
> + TEST_STRING(func_int_ptr, "_ZTSFvPiE");
> + TEST_STRING(func_int_array, "_ZTSFvPiE");
> + TEST_STRING(func_long_ptr, "_ZTSFvPlE");
> +
> + /* Test functions with const qualifiers */
> + extern void func_const_void_ptr(const void *x);
> + extern void func_const_char_ptr(const char *x);
> + extern void func_const_short_ptr(const short *x);
> + extern void func_const_int_ptr(const int *x);
> + extern void func_const_long_ptr(const long *x);
> + TEST_STRING(func_const_void_ptr, "_ZTSFvPKvE");
> + TEST_STRING(func_const_char_ptr, "_ZTSFvPKcE");
> + TEST_STRING(func_const_short_ptr, "_ZTSFvPKsE");
> + TEST_STRING(func_const_int_ptr, "_ZTSFvPKiE");
> + TEST_STRING(func_const_long_ptr, "_ZTSFvPKlE");
> +
> + /* Test nested pointers */
> + extern void func_int_ptr_ptr(int **x);
> + extern void func_char_ptr_ptr(char **x);
> + TEST_STRING(func_int_ptr_ptr, "_ZTSFvPPiE");
> + TEST_STRING(func_char_ptr_ptr, "_ZTSFvPPcE");
> +
> + /* Test multiple parameters */
> + extern void func_int_char(int x, char y);
> + extern void func_char_int(char x, int y);
> + extern void func_two_int(int x, int y);
> + TEST_STRING(func_int_char, "_ZTSFvicE");
> + TEST_STRING(func_char_int, "_ZTSFvciE");
> + TEST_STRING(func_two_int, "_ZTSFviiE");
> +
> + /* Test return types */
> + extern int func_return_int(void);
> + extern char func_return_char(void);
> + extern void* func_return_ptr(void);
> + TEST_STRING(func_return_int, "_ZTSFivE");
> + TEST_STRING(func_return_char, "_ZTSFcvE");
> + TEST_STRING(func_return_ptr, "_ZTSFPvvE");
> +
> + /* Test function pointer parameters */
> + extern void func_fptr_void(void (*fp)(void));
> + extern void func_fptr_int(void (*fp)(int));
> + extern void func_fptr_ret_int(int (*fp)(void));
> + TEST_STRING(func_fptr_void, "_ZTSFvPFvvEE");
> + TEST_STRING(func_fptr_int, "_ZTSFvPFviEE");
> + TEST_STRING(func_fptr_ret_int, "_ZTSFvPFivEE");
> +
> + /* Test variadic functions */
> + struct audit_context { int dummy; };
> + extern void func_variadic_simple(const char *fmt, ...);
> + extern void func_variadic_mixed(int x, const char *fmt, ...);
> + extern void func_variadic_multi(int x, char y, const char *fmt, ...);
> + extern void audit_log_pattern(struct audit_context *ctx, unsigned int gfp_mask,
> + int type, const char *fmt, ...);
> + TEST_STRING(func_variadic_simple, "_ZTSFvPKczE");
> + TEST_STRING(func_variadic_mixed, "_ZTSFviPKczE");
> + TEST_STRING(func_variadic_multi, "_ZTSFvicPKczE");
> + TEST_STRING(audit_log_pattern, "_ZTSFvP13audit_contextjiPKczE");
> +
> + /* Test mixed const/non-const */
> + extern void func_const_mixed(int x, const char *fmt);
> + TEST_STRING(func_const_mixed, "_ZTSFviPKcE");
> +
> + /* Test named struct types */
> + struct test_struct_a { int x; };
> + struct test_struct_b { char y; };
> + struct test_struct_c { void *ptr; };
> + TEST_STRING(struct test_struct_a, "_ZTS13test_struct_a");
> + extern void func_struct_a_ptr(struct test_struct_a *x);
> + extern void func_struct_b_ptr(struct test_struct_b *x);
> + extern void func_struct_c_ptr(struct test_struct_c *x);
> + TEST_STRING(func_struct_a_ptr, "_ZTSFvP13test_struct_aE");
> + TEST_STRING(func_struct_b_ptr, "_ZTSFvP13test_struct_bE");
> + TEST_STRING(func_struct_c_ptr, "_ZTSFvP13test_struct_cE");
> +
> + /* Test const named struct types */
> + extern void func_const_struct_a_ptr(const struct test_struct_a *x);
> + extern void func_const_struct_b_ptr(const struct test_struct_b *x);
> + extern void func_const_struct_c_ptr(const struct test_struct_c *x);
> + TEST_STRING(func_const_struct_a_ptr, "_ZTSFvPK13test_struct_aE");
> + TEST_STRING(func_const_struct_b_ptr, "_ZTSFvPK13test_struct_bE");
> + TEST_STRING(func_const_struct_c_ptr, "_ZTSFvPK13test_struct_cE");
> +
> + /* Test named union types */
> + union test_union_a { int x; float y; };
> + union test_union_b { char a; void *b; };
> + TEST_STRING(union test_union_a, "_ZTS12test_union_a");
> + extern void func_union_a_ptr(union test_union_a *x);
> + extern void func_union_b_ptr(union test_union_b *x);
> + TEST_STRING(func_union_a_ptr, "_ZTSFvP12test_union_aE");
> + TEST_STRING(func_union_b_ptr, "_ZTSFvP12test_union_bE");
> +
> + /* Test enum types: distinct from int */
> + enum test_enum_a { ENUM_A_VAL };
> + enum test_enum_b { ENUM_B_VAL };
> + TEST_STRING(enum test_enum_a, "_ZTS11test_enum_a");
> + extern void func_enum_a_ptr(enum test_enum_a *x);
> + extern void func_enum_b_ptr(enum test_enum_b *x);
> + TEST_STRING(func_enum_a_ptr, "_ZTSFvP11test_enum_aE");
> + TEST_STRING(func_enum_b_ptr, "_ZTSFvP11test_enum_bE");
> +
> + /* Test union member discrimination */
> + struct tasklet {
> + int state;
> + union {
> + void (*func)(unsigned long data);
> + void (*callback)(struct tasklet *t);
> + };
> + unsigned long data;
> + } tasklet_instance;
> + TEST_STRING(tasklet_instance, "_ZTS7tasklet");
> + struct tasklet *p = &tasklet_instance;
> + extern void tasklet_callback_function(struct tasklet *t);
> + extern void tasklet_func_function(unsigned long data);
> + TEST_STRING(tasklet_func_function, "_ZTSFvmE");
> + TEST_STRING(*p->func, "_ZTSFvmE");
> + TEST_STRING(tasklet_callback_function, "_ZTSFvP7taskletE");
> + TEST_STRING(*p->callback, "_ZTSFvP7taskletE");
> +
> + /* Test struct return pointers */
> + extern struct test_struct_a* func_ret_struct_a_ptr(void);
> + extern struct test_struct_b* func_ret_struct_b_ptr(void);
> + extern struct test_struct_c* func_ret_struct_c_ptr(void);
> + TEST_STRING(func_ret_struct_a_ptr, "_ZTSFP13test_struct_avE");
> + TEST_STRING(func_ret_struct_b_ptr, "_ZTSFP13test_struct_bvE");
> + TEST_STRING(func_ret_struct_c_ptr, "_ZTSFP13test_struct_cvE");
> +
> + /* Test struct by-value parameters */
> + extern void func_struct_a_val(struct test_struct_a x);
> + extern void func_struct_b_val(struct test_struct_b x);
> + extern void func_struct_c_val(struct test_struct_c x);
> + TEST_STRING(func_struct_a_val, "_ZTSFv13test_struct_aE");
> + TEST_STRING(func_struct_b_val, "_ZTSFv13test_struct_bE");
> + TEST_STRING(func_struct_c_val, "_ZTSFv13test_struct_cE");
> +
> + /* Test struct return by-value */
> + extern struct test_struct_a func_ret_struct_a_val(void);
> + extern struct test_struct_b func_ret_struct_b_val(void);
> + extern struct test_struct_c func_ret_struct_c_val(void);
> + TEST_STRING(func_ret_struct_a_val, "_ZTSF13test_struct_avE");
> + TEST_STRING(func_ret_struct_b_val, "_ZTSF13test_struct_bvE");
> + TEST_STRING(func_ret_struct_c_val, "_ZTSF13test_struct_cvE");
> +
> + /* Test mixed struct parameters */
> + extern void func_struct_a_b(struct test_struct_a *a, struct test_struct_b *b);
> + extern void func_struct_b_a(struct test_struct_b *b, struct test_struct_a *a);
> + TEST_STRING(func_struct_a_b, "_ZTSFvP13test_struct_aP13test_struct_bE");
> + TEST_STRING(func_struct_b_a, "_ZTSFvP13test_struct_bP13test_struct_aE");
> +
> + /* Test anonymous struct typedefs */
> + typedef struct { int x; } typedef_struct_x;
> + typedef struct { int y; } typedef_struct_y;
> + TEST_STRING(typedef_struct_x, "_ZTS16typedef_struct_x");
> + extern void func_typedef_x_ptr(typedef_struct_x *x);
> + extern void func_typedef_y_ptr(typedef_struct_y *y);
> + TEST_STRING(func_typedef_x_ptr, "_ZTSFvP16typedef_struct_xE");
> + TEST_STRING(func_typedef_y_ptr, "_ZTSFvP16typedef_struct_yE");
> + extern void func_typedef_x(typedef_struct_x x);
> + TEST_STRING(func_typedef_x, "_ZTSFv16typedef_struct_xE");
> +
> + /* Test anonymous union typedefs */
> + typedef union { int x; short a; } typedef_union_x;
> + typedef union { int y; short b; } typedef_union_y;
> + TEST_STRING(typedef_union_x, "_ZTS15typedef_union_x");
> + extern void func_typedef_union_x_ptr(typedef_union_x *x);
> + extern void func_typedef_union_y_ptr(typedef_union_y *y);
> + TEST_STRING(func_typedef_union_x_ptr, "_ZTSFvP15typedef_union_xE");
> + TEST_STRING(func_typedef_union_y_ptr, "_ZTSFvP15typedef_union_yE");
> + extern void func_typedef_union_x(typedef_union_x x);
> + TEST_STRING(func_typedef_union_x, "_ZTSFv15typedef_union_xE");
> +
> + /* Test anonymous enum typedefs */
> + typedef enum { STEP_1, STEP_2 } typedef_enum_x;
> + typedef enum { STEP_A, STEP_B } typedef_enum_y;
> + TEST_STRING(typedef_enum_x, "_ZTS14typedef_enum_x");
> + extern void func_typedef_enum_x_ptr(typedef_enum_x *x);
> + extern void func_typedef_enum_y_ptr(typedef_enum_y *y);
> + TEST_STRING(func_typedef_enum_x_ptr, "_ZTSFvP14typedef_enum_xE");
> + TEST_STRING(func_typedef_enum_y_ptr, "_ZTSFvP14typedef_enum_yE");
> + extern void func_typedef_enum_x(typedef_enum_x x);
> + TEST_STRING(func_typedef_enum_x, "_ZTSFv14typedef_enum_xE");
> +
> + /* Test basic typedef vs open-coded function types: should be the same. */
> + typedef void (*func_type_typedef)(int, char);
> + TEST_STRING(func_type_typedef, "_ZTSPFvicE");
> + extern void func_with_typedef_param(func_type_typedef fp);
> + extern void func_with_opencoded_param(void (*fp)(int, char));
> + TEST_STRING(func_with_typedef_param, "_ZTSFvPFvicEE");
> + TEST_STRING(func_with_opencoded_param, "_ZTSFvPFvicEE");
> +
> + /* Test return function pointer types */
> + typedef int (*ret_func_type_typedef)(void);
> + TEST_STRING(ret_func_type_typedef, "_ZTSPFivE");
> + extern ret_func_type_typedef func_ret_typedef_param(void);
> + extern int (*func_ret_opencoded_param(void))(void);
> + TEST_STRING(func_ret_typedef_param, "_ZTSFPFivEvE");
> + TEST_STRING(func_ret_opencoded_param, "_ZTSFPFivEvE");
> +
> + /* Test additional type combos */
> + extern void func_float(float x);
> + extern void func_double_ptr(double *x);
> + extern void func_float_ptr(float *x);
> + extern void func_void_ptr_ptr(void **x);
> + extern void func_ptr_val(int *x, int y);
> + extern void func_val_ptr(int x, int *y);
> + extern float func_return_float(void);
> + extern double func_return_double(void);
> + TEST_STRING(func_float, "_ZTSFvfE");
> + TEST_STRING(func_double_ptr, "_ZTSFvPdE");
> + TEST_STRING(func_float_ptr, "_ZTSFvPfE");
> + TEST_STRING(func_void_ptr_ptr, "_ZTSFvPPvE");
> + TEST_STRING(func_ptr_val, "_ZTSFvPiiE");
> + TEST_STRING(func_val_ptr, "_ZTSFviPiE");
> + TEST_STRING(func_return_float, "_ZTSFfvE");
> + TEST_STRING(func_return_double, "_ZTSFdvE");
> +
> + /* Test VLA types: should be all the same. */
> + extern void func_vla_1d(int n, int arr[n]);
> + extern void func_vla_empty(int n, int arr[]);
> + extern void func_vla_ptr(int n, int *arr);
> + TEST_STRING(func_vla_1d, "_ZTSFviPiE");
> + TEST_STRING(func_vla_empty, "_ZTSFviPiE");
> + TEST_STRING(func_vla_ptr, "_ZTSFviPiE");
> +
> + /* Test 2D VLA with fixed dimension: should be all the same. */
> + extern void func_vla_2d_first(int n, int arr[n][10]);
> + extern void func_vla_2d_empty(int n, int arr[][10]);
> + extern void func_vla_2d_ptr(int n, int (*arr)[10]);
> + TEST_STRING(func_vla_2d_first, "_ZTSFviPA10_iE");
> + TEST_STRING(func_vla_2d_empty, "_ZTSFviPA10_iE");
> + TEST_STRING(func_vla_2d_ptr, "_ZTSFviPA10_iE");
> +
> + /* Test 2D VLA with both dimensions variable: should be all the same. */
> + extern void func_vla_2d_both(int rows, int cols, int arr[rows][cols]);
> + extern void func_vla_2d_second(int rows, int cols, int arr[][cols]);
> + extern void func_vla_2d_star(int rows, int cols, int arr[*][cols]);
> + TEST_STRING(func_vla_2d_both, "_ZTSFviiPA_iE");
> + TEST_STRING(func_vla_2d_second, "_ZTSFviiPA_iE");
> + TEST_STRING(func_vla_2d_star, "_ZTSFviiPA_iE");
> +
> + /* Test recursive typedef canonicalization */
> + struct recursive_struct_test { int field; };
> + typedef struct recursive_struct_test recursive_struct_typedef_1;
> + typedef recursive_struct_typedef_1 recursive_struct_typedef_2;
> + extern void func_recursive_struct_test(struct recursive_struct_test *x);
> + TEST_STRING(func_recursive_struct_test, "_ZTSFvP21recursive_struct_testE");
> +
> + /* Test anonymous struct, union, enum types */
> + struct { int a; short b; } anon_struct;
> + union { int x; float y; } anon_union;
> + enum { ANON_VAL1, ANON_VAL2 } anon_enum;
> + TEST_STRING(anon_struct, "_ZTS3$_0"); // <length>$_<counter>
> + TEST_STRING(anon_union, "_ZTS3$_1"); // <length>$_<counter>
> + TEST_STRING(anon_enum, "_ZTS3$_2"); // <length>$_<counter>
> +
> + printf("\n================================================================\n");
> + printf("Passed: %d Failed: %d (%d total tests)\n", pass, fail, pass + fail);
> + return fail;
> +}
> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> index e7dd4602ac11..94f2c2001ad5 100644
> --- a/gcc/c-family/c-common.cc
> +++ b/gcc/c-family/c-common.cc
> @@ -461,6 +461,8 @@ const struct c_common_resword c_common_reswords[] =
> { "__builtin_stdc_trailing_zeros", RID_BUILTIN_STDC, D_CONLY },
> { "__builtin_tgmath", RID_BUILTIN_TGMATH, D_CONLY },
> { "__builtin_offsetof", RID_OFFSETOF, 0 },
> + { "__builtin_typeinfo_hash", RID_BUILTIN_TYPEINFO_HASH, D_CONLY },
> + { "__builtin_typeinfo_name", RID_BUILTIN_TYPEINFO_NAME, D_CONLY },
> { "__builtin_types_compatible_p", RID_TYPES_COMPATIBLE_P, D_CONLY },
> { "__builtin_c23_va_start", RID_C23_VA_START, D_C23 },
> { "__builtin_va_arg", RID_VA_ARG, 0 },
> diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
> index e8b64948bf69..996fb576ac7c 100644
> --- a/gcc/c/c-parser.cc
> +++ b/gcc/c/c-parser.cc
> @@ -77,6 +77,7 @@ along with GCC; see the file COPYING3. If not see
> #include "asan.h"
> #include "c-family/c-ubsan.h"
> #include "gcc-urlifier.h"
> +#include "kcfi-typeinfo.h"
>
> /* We need to walk over decls with incomplete struct/union/enum types
> after parsing the whole translation unit.
> @@ -11017,6 +11018,38 @@ c_parser_has_attribute_expression (c_parser *parser)
> return result;
> }
>
> +/* Parse the single type name argument of a builtin that takes a type name.
> + Returns true on success and stores the parsed type in *OUT_TYPE.
> + If successful, *OUT_CLOSE_PAREN_LOC is written with the location of
> + the closing parenthesis. */
> +
> +static bool
> +c_parser_get_builtin_type_arg (c_parser *parser, const char *bname,
> + tree *out_type, location_t *out_close_paren_loc)
> +{
> + matching_parens parens;
> + if (!parens.require_open (parser))
> + return false;
> +
> + struct c_type_name *type_name = c_parser_type_name (parser);
> + if (type_name == NULL)
> + {
> + error_at (c_parser_peek_token (parser)->location,
> + "expected type name in %qs", bname);
> + return false;
> + }
> +
> + *out_close_paren_loc = c_parser_peek_token (parser)->location;
> + parens.skip_until_found_close (parser);
> +
> + tree type = groktypename (type_name, NULL, NULL);
> + if (type == error_mark_node)
> + return false;
> +
> + *out_type = type;
> + return true;
> +}
> +
> /* Helper function to read arguments of builtins which are interfaces
> for the middle-end nodes like COMPLEX_EXPR, VEC_PERM_EXPR and
> others. The name of the builtin is passed using BNAME parameter.
> @@ -12025,6 +12058,45 @@ c_parser_postfix_expression (c_parser *parser)
> set_c_expr_source_range (&expr, loc, close_paren_loc);
> }
> break;
> + case RID_BUILTIN_TYPEINFO_NAME:
> + {
> + c_parser_consume_token (parser);
> + location_t close_paren_loc;
> + tree type;
> + if (!c_parser_get_builtin_type_arg (parser,
> + "__builtin_typeinfo_name",
> + &type, &close_paren_loc))
> + {
> + expr.set_error ();
> + break;
> + }
> +
> + /* Call the typeinfo name function. */
> + std::string type_name = typeinfo_get_name (type);
> + expr.value = build_string_literal (type_name.length () + 1,
> + type_name.c_str ());
> + set_c_expr_source_range (&expr, loc, close_paren_loc);
> + }
> + break;
> + case RID_BUILTIN_TYPEINFO_HASH:
> + {
> + c_parser_consume_token (parser);
> + location_t close_paren_loc;
> + tree type;
> + if (!c_parser_get_builtin_type_arg (parser,
> + "__builtin_typeinfo_hash",
> + &type, &close_paren_loc))
> + {
> + expr.set_error ();
> + break;
> + }
> +
> + /* Call the typeinfo hash function. */
> + uint32_t type_hash = typeinfo_get_hash (type);
> + expr.value = build_int_cst (unsigned_type_node, type_hash);
> + set_c_expr_source_range (&expr, loc, close_paren_loc);
> + }
> + break;
> case RID_BUILTIN_TGMATH:
> {
> vec<c_expr_t, va_gc> *cexpr_list;
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 382295834035..7cddea1ed6c1 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -17547,6 +17547,100 @@ which will cause a @code{NULL} pointer to be used for the unsafe case.
>
> @enddefbuiltin
>
> +@...builtin{{unsigned int} __builtin_typeinfo_hash (@var{type})}
> +
> +The built-in function @code{__builtin_typeinfo_hash} returns a hash value
> +for the given type @var{type} (which is a type, not an expression). The hash
> +is computed using the FNV-1a algorithm on the type's mangled name representation,
> +which follows a subset of the Itanium C++ ABI conventions adapted for C types.
> +(See @code{__buitin_typeinfo_name} for the string representation.)
> +
> +This built-in is primarily intended for kernel control flow integrity (KCFI)
> +implementations and other type-aware runtime systems that need to generate
> +consistent type identifiers. The hash value is a 32-bit unsigned integer.
> +
> +Key characteristics of the hash:
> +@...mize @bullet
> +@...m
> +The hash is consistent for the same type across different translation units.
> +@...m
> +Typedefs are recursively canonicalized down to integral type name or named
> +struct, union, or enum tag name.
> +@...m
> +Typedefs of anonymous structs, unions, and enums preserve the typedef name
> +in the hash calculation (e.g., @code{typedef struct @{ int x; @} foo_t;}
> +uses @code{foo_t} in the hash).
> +@...m
> +Type qualifiers (@code{const}, @code{volatile}, @code{restrict}) affect
> +the hash value.
> +@...m
> +Function types include parameter types and variadic markers in the hash.
> +@end itemize
> +
> +For example:
> +@...llexample
> +typedef struct @{ int x; @} mytype_t;
> +unsigned int hash1 = __builtin_typeinfo_hash(mytype_t);
> +unsigned int hash2 = __builtin_typeinfo_hash(struct @{ int x; @});
> +/* hash1 != hash2 because the typedef name is preserved */
> +
> +void func(int x, char y);
> +unsigned int hash3 = __builtin_typeinfo_hash(typeof(func));
> +/* Returns hash for function type "void(int, char)" */
> +@end smallexample
> +
> +@...h{Note:} This construct is only available for C@. For C++, see
> +@...e{std::type_info::hash_code}.
> +
> +@...defbuiltin
> +
> +@...builtin{{const char *} __builtin_typeinfo_name (@var{type})}
> +
> +The built-in function @code{__builtin_typeinfo_name} returns a string
> +containing the mangled name representation of the given type @var{type}
> +(which is a type, not an expression). The string follows a subset of the
> +Itanium C++ ABI mangling conventions adapted for C types. (See
> +@...e{__buitin_typeinfo_hash} for the unsigned 32-bit hash representation.)
> +
> +The returned string is a compile-time constant suitable for use in
> +string comparisons, debugging output, or other type introspection needs.
> +The string begins with @code{_ZTS} followed by the encoded type information.
> +
> +Mangling examples:
> +@...mize @bullet
> +@...m
> +@...e{int} becomes @code{"_ZTSi"}
> +@...m
> +@...e{char *} becomes @code{"_ZTSPc"}
> +@...m
> +@...e{const int} becomes @code{"_ZTSKi"}
> +@...m
> +@...e{int[10]} becomes @code{"_ZTSA10_i"}
> +@...m
> +@...e{void (*)(int)} becomes @code{"_ZTSPFviE"}
> +@...m
> +@...e{struct foo} becomes @code{"_ZTS3foo"}
> +@...m
> +@...e{typedef struct @{ int x; @} bar_t;} becomes @code{"_ZTS5bar_t"}
> +@end itemize
> +
> +The mangling preserves typedef names for anonymous compound types, which
> +is particularly useful for distinguishing between different typedefs of
> +structurally identical anonymous types:
> +
> +@...llexample
> +typedef struct @{ int x; @} type_a;
> +typedef struct @{ int x; @} type_b;
> +const char *name_a = __builtin_typeinfo_name(type_a); /* "_ZTS6type_a" */
> +const char *name_b = __builtin_typeinfo_name(type_b); /* "_ZTS6type_b" */
> +/* name_a and name_b are different despite identical structure */
> +@end smallexample
> +
> +@...h{Note:} This construct is only available for C@. For C++, see
> +@...e{std::type_info::name}.
> +
> +@...defbuiltin
> +
> @defbuiltin{int __builtin_types_compatible_p (@var{type1}, @var{type2})}
>
> You can use the built-in function @code{__builtin_types_compatible_p} to
> diff --git a/gcc/kcfi-typeinfo.cc b/gcc/kcfi-typeinfo.cc
> new file mode 100644
> index 000000000000..24099c42cc2e
> --- /dev/null
> +++ b/gcc/kcfi-typeinfo.cc
> @@ -0,0 +1,475 @@
> +/* KCFI-compatible type mangling, based on Itanium C++ ABI.
> + Copyright (C) 2025 Free Software Foundation, Inc.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it under
> +the terms of the GNU General Public License as published by the Free
> +Software Foundation; either version 3, or (at your option) any later
> +version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY
> +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3. If not see
> +<http://www.gnu.org/licenses/>. */
> +
> +/* Produces typeinfo mangling similar to Itanium C++ Mangling ABI, but
> + limited to types exposed within GCC for C language handling. The
> + hashes are used by KCFI (and future type-aware allocator support).
> + The strings are used for testing and debugging. */
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +#include "tree.h"
> +#include "diagnostic-core.h"
> +#include "stringpool.h"
> +#include "stor-layout.h"
> +#include "print-tree.h"
> +#include "kcfi-typeinfo.h"
> +
> +/* Helper to update FNV-1a hash with a single character. */
> +
> +static inline void
> +fnv1a_hash_char (uint32_t *hash_state, unsigned char c)
> +{
> + *hash_state ^= c;
> + *hash_state *= 16777619U; /* FNV-1a 32-bit prime. */
> +}
> +
> +/* Helper to append character to optional string and update hash using
> + FNV-1a. */
> +
> +static void
> +append_char (char c, std::string *out_str, uint32_t *hash_state)
> +{
> + if (out_str)
> + *out_str += c;
> + if (!hash_state)
> + return;
> + fnv1a_hash_char (hash_state, (unsigned char) c);
> +}
> +
> +/* Helper to append string to optional string and update hash using
> + FNV-1a. */
> +
> +static void
> +append_string (const char *str, std::string *out_str, uint32_t *hash_state)
> +{
> + if (out_str)
> + *out_str += str;
> + if (!hash_state)
> + return;
> + for (const char *p = str; *p; p++)
> + fnv1a_hash_char (hash_state, (unsigned char) *p);
> +}
> +
> +/* Forward declaration for recursive type mangling. */
> +
> +static void mangle_type (tree type, std::string *out_str, uint32_t *hash_state);
> +
> +/* Mangle a builtin type following Itanium C++ ABI for C types. */
> +
> +static void
> +mangle_builtin_type (tree type, std::string *out_str, uint32_t *hash_state)
> +{
> + gcc_assert (type != NULL_TREE);
> +
> + switch (TREE_CODE (type))
> + {
> + case VOID_TYPE:
> + append_char ('v', out_str, hash_state);
> + return;
> +
> + case BOOLEAN_TYPE:
> + append_char ('b', out_str, hash_state);
> + return;
> +
> + case INTEGER_TYPE:
> + if (type == char_type_node)
> + append_char ('c', out_str, hash_state);
> + else if (type == signed_char_type_node)
> + append_char ('a', out_str, hash_state);
> + else if (type == unsigned_char_type_node)
> + append_char ('h', out_str, hash_state);
> + else if (type == short_integer_type_node)
> + append_char ('s', out_str, hash_state);
> + else if (type == short_unsigned_type_node)
> + append_char ('t', out_str, hash_state);
> + else if (type == integer_type_node)
> + append_char ('i', out_str, hash_state);
> + else if (type == unsigned_type_node)
> + append_char ('j', out_str, hash_state);
> + else if (type == long_integer_type_node)
> + append_char ('l', out_str, hash_state);
> + else if (type == long_unsigned_type_node)
> + append_char ('m', out_str, hash_state);
> + else if (type == long_long_integer_type_node)
> + append_char ('x', out_str, hash_state);
> + else if (type == long_long_unsigned_type_node)
> + append_char ('y', out_str, hash_state);
> + else
> + {
> + /* Fallback for other integer types - use precision-based
> + encoding. */
> + append_char ('i', out_str, hash_state);
> + append_string (std::to_string (TYPE_PRECISION (type)).c_str (),
> + out_str, hash_state);
> + }
> + return;
> +
> + case REAL_TYPE:
> + if (type == float_type_node)
> + append_char ('f', out_str, hash_state);
> + else if (type == double_type_node)
> + append_char ('d', out_str, hash_state);
> + else if (type == long_double_type_node)
> + append_char ('e', out_str, hash_state);
> + else
> + {
> + /* Fallback for other real types. */
> + append_char ('f', out_str, hash_state);
> + append_string (std::to_string (TYPE_PRECISION (type)).c_str (),
> + out_str, hash_state);
> + }
> + return;
> +
> + case VECTOR_TYPE:
> + {
> + /* Handle vector types:
> + Dv<num-elements>_<element-type-encoding>
> + Example: uint8x16_t -> Dv16_h (vector of 16 unsigned char) */
> + tree vector_size = TYPE_SIZE_UNIT (type);
> + tree element_type = TREE_TYPE (type);
> + tree element_size = TYPE_SIZE_UNIT (element_type);
> +
> + if (vector_size && element_size
> + && TREE_CODE (vector_size) == INTEGER_CST
> + && TREE_CODE (element_size) == INTEGER_CST)
> + {
> + append_char ('D', out_str, hash_state);
> + append_char ('v', out_str, hash_state);
> +
> + unsigned HOST_WIDE_INT vec_bytes = tree_to_uhwi (vector_size);
> + unsigned HOST_WIDE_INT elem_bytes = tree_to_uhwi (element_size);
> + unsigned HOST_WIDE_INT num_elements = vec_bytes / elem_bytes;
> +
> + /* Append number of elements. */
> + append_string (std::to_string (num_elements).c_str (),
> + out_str, hash_state);
> + append_char ('_', out_str, hash_state);
> +
> + /* Recursively mangle the element type. */
> + mangle_type (element_type, out_str, hash_state);
> + return;
> + }
> + /* Fail for vectors with unknown size. */
> + }
> + break;
> +
> + default:
> + break;
> + }
> +
> + /* Unknown builtin type: this should never happen in a well-formed C. */
> + debug_tree (type);
> + internal_error ("mangle: Unknown builtin type - please report this as a bug");
> +}
> +
> +/* Canonicalize typedef types to their underlying named struct/union types. */
> +
> +static tree
> +canonicalize_typedef_type (tree type)
> +{
> + /* Handle typedef types: canonicalize to named structs when possible. */
> + if (TYPE_NAME (type) && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL)
> + {
> + tree type_decl = TYPE_NAME (type);
> +
> + /* Check if this is a typedef (not the original struct declaration) */
> + if (DECL_ORIGINAL_TYPE (type_decl))
> + {
> + tree original_type = DECL_ORIGINAL_TYPE (type_decl);
> +
> + /* Handle struct/union/enum types. */
> + if (TREE_CODE (original_type) == RECORD_TYPE
> + || TREE_CODE (original_type) == UNION_TYPE
> + || TREE_CODE (original_type) == ENUMERAL_TYPE)
> + {
> + /* Preserve typedef of anonymous struct/union/enum types. */
> + if (!TYPE_NAME (original_type))
> + return type;
> +
> + /* Named compound type: canonicalize to it. */
> + return canonicalize_typedef_type (original_type);
> + }
> +
> + /* For basic type typedefs (e.g., u8 -> unsigned char),
> + canonicalize to original type. */
> + if (TREE_CODE (original_type) == INTEGER_TYPE
> + || TREE_CODE (original_type) == REAL_TYPE
> + || TREE_CODE (original_type) == POINTER_TYPE
> + || TREE_CODE (original_type) == ARRAY_TYPE
> + || TREE_CODE (original_type) == FUNCTION_TYPE
> + || TREE_CODE (original_type) == METHOD_TYPE
> + || TREE_CODE (original_type) == BOOLEAN_TYPE
> + || TREE_CODE (original_type) == COMPLEX_TYPE
> + || TREE_CODE (original_type) == VECTOR_TYPE)
> + {
> + /* Recursively canonicalize in case the original type is
> + also a typedef. */
> + return canonicalize_typedef_type (original_type);
> + }
> + }
> + }
> +
> + return type;
> +}
> +
> +/* Recursively mangle a C type following Itanium C++ ABI. */
> +
> +static void
> +mangle_type (tree type, std::string *out_str, uint32_t *hash_state)
> +{
> + gcc_assert (type != NULL_TREE);
> +
> + /* Canonicalize typedef types to their underlying named struct types. */
> + type = canonicalize_typedef_type (type);
> +
> + /* Save original qualified type for cases where we need typedef
> + information. */
> + tree qualified_type = type;
> +
> + /* Centralized qualifier handling: emit qualifiers for this type,
> + then continue with unqualified version. */
> + if (TYPE_QUALS (type) != TYPE_UNQUALIFIED)
> + {
> + /* Emit qualifiers in Itanium ABI order: restrict, volatile, const. */
> + if (TYPE_QUALS (type) & TYPE_QUAL_RESTRICT)
> + append_char ('r', out_str, hash_state);
> + if (TYPE_QUALS (type) & TYPE_QUAL_VOLATILE)
> + append_char ('V', out_str, hash_state);
> + if (TYPE_QUALS (type) & TYPE_QUAL_CONST)
> + append_char ('K', out_str, hash_state);
> +
> + /* Get unqualified version for further processing. */
> + type = TYPE_MAIN_VARIANT (type);
> + }
> +
> + switch (TREE_CODE (type))
> + {
> + case POINTER_TYPE:
> + {
> + /* Pointer type: 'P' + pointed-to type. */
> + append_char ('P', out_str, hash_state);
> +
> + /* Recursively mangle the pointed-to type. */
> + tree pointed_to_type = TREE_TYPE (type);
> + mangle_type (pointed_to_type, out_str, hash_state);
> + break;
> + }
> +
> + case ARRAY_TYPE:
> + /* Array type: 'A' + size + '_' + element type (simplified). */
> + append_char ('A', out_str, hash_state);
> + if (TYPE_DOMAIN (type) && TYPE_MAX_VALUE (TYPE_DOMAIN (type)))
> + {
> + tree max_val = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
> + /* Check if array size is compile-time constant to handle VLAs. */
> + if (TREE_CODE (max_val) == INTEGER_CST && tree_fits_shwi_p (max_val))
> + {
> + HOST_WIDE_INT size = tree_to_shwi (max_val) + 1;
> + append_string (std::to_string ((long) size).c_str (),
> + out_str, hash_state);
> + }
> + /* For VLAs or non-constant dimensions, emit empty size (A_). */
> + append_char ('_', out_str, hash_state);
> + }
> + else
> + {
> + /* No domain or no max value: emit A_. */
> + append_char ('_', out_str, hash_state);
> + }
> + mangle_type (TREE_TYPE (type), out_str, hash_state);
> + break;
> +
> + case REFERENCE_TYPE:
> + /* Reference type: 'R' + referenced type.
> + Note: We must handle references to builtin types including compiler
> + builtins like __builtin_va_list used in functions like va_start. */
> + append_char ('R', out_str, hash_state);
> + mangle_type (TREE_TYPE (type), out_str, hash_state);
> + break;
> +
> + case FUNCTION_TYPE:
> + {
> + /* Function type: 'F' + return type + parameter types + 'E' */
> + append_char ('F', out_str, hash_state);
> + mangle_type (TREE_TYPE (type), out_str, hash_state);
> +
> + /* Add parameter types. */
> + tree param_types = TYPE_ARG_TYPES (type);
> +
> + if (param_types == NULL_TREE)
> + {
> + /* func () - no parameter list (could be variadic). */
> + }
> + else
> + {
> + bool found_real_params = false;
> + for (tree param = param_types; param; param = TREE_CHAIN (param))
> + {
> + tree param_type = TREE_VALUE (param);
> + if (param_type == void_type_node)
> + {
> + /* Check if this is the first parameter (explicit void) or a
> + sentinel. */
> + if (!found_real_params)
> + {
> + /* func (void) - explicit empty parameter list.
> + Mangle void to distinguish from variadic func (). */
> + mangle_type (void_type_node, out_str, hash_state);
> + }
> + /* If we found real params before this void, it's a sentinel
> + so stop here. */
> + break;
> + }
> +
> + found_real_params = true;
> +
> + /* For value parameters, ignore const/volatile qualifiers as
> + they don't affect the calling convention. "const int" and
> + "int" are passed identically by value. */
> + tree canonical_param_type = param_type;
> +
> + if (TREE_CODE (param_type) != POINTER_TYPE
> + && TREE_CODE (param_type) != REFERENCE_TYPE
> + && TREE_CODE (param_type) != ARRAY_TYPE)
> + {
> + /* For non-pointer/reference value parameters, strip
> + qualifiers by default. */
> + canonical_param_type = TYPE_MAIN_VARIANT (param_type);
> +
> + /* Exception: preserve typedef information for anonymous
> + compound types. */
> + if (TYPE_NAME (param_type)
> + && TREE_CODE (TYPE_NAME (param_type)) == TYPE_DECL
> + && DECL_ORIGINAL_TYPE (TYPE_NAME (param_type)))
> + {
> + tree original_type
> + = DECL_ORIGINAL_TYPE (TYPE_NAME (param_type));
> + if ((TREE_CODE (original_type) == RECORD_TYPE
> + || TREE_CODE (original_type) == UNION_TYPE
> + || TREE_CODE (original_type) == ENUMERAL_TYPE)
> + && !TYPE_NAME (original_type))
> + {
> + /* Preserve typedef of an anonymous
> + struct/union/enum. */
> + canonical_param_type = param_type;
> + }
> + }
> + }
> +
> + mangle_type (canonical_param_type, out_str, hash_state);
> + }
> + }
> +
> + /* Check if this is a variadic function and add 'z' marker. */
> + if (stdarg_p (type))
> + {
> + append_char ('z', out_str, hash_state);
> + }
> +
> + append_char ('E', out_str, hash_state);
> + break;
> + }
> +
> + case RECORD_TYPE:
> + case UNION_TYPE:
> + case ENUMERAL_TYPE:
> + {
> + /* Struct/union/enum: use simplified representation for C types. */
> + const char *name = NULL;
> +
> + /* For compound types, use the original qualified type to preserve
> + typedef info. */
> + if (TYPE_QUALS (qualified_type) != TYPE_UNQUALIFIED)
> + {
> + type = qualified_type;
> + }
> +
> + if (TYPE_NAME (type))
> + {
> + if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL)
> + {
> + /* TYPE_DECL case: both named structs and typedef structs. */
> + tree decl_name = DECL_NAME (TYPE_NAME (type));
> + if (decl_name && TREE_CODE (decl_name) == IDENTIFIER_NODE)
> + {
> + name = IDENTIFIER_POINTER (decl_name);
> + }
> + }
> + else if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
> + {
> + /* Direct identifier case. */
> + name = IDENTIFIER_POINTER (TYPE_NAME (type));
> + }
> + }
> +
> + if (name)
> + {
> + append_string (std::to_string (strlen (name)).c_str (),
> + out_str, hash_state);
> + append_string (name, out_str, hash_state);
> + break;
> + }
> +
> + /* If no name found, use anonymous type format: <length>$_<counter>. */
> + static unsigned anon_counter = 0;
> + std::string anon_name = "$_" + std::to_string (anon_counter++);
> +
> + append_string (std::to_string (anon_name.length ()).c_str (),
> + out_str, hash_state);
> + append_string (anon_name.c_str (), out_str, hash_state);
> + break;
> + }
> +
> + default:
> + /* Handle builtin types. */
> + mangle_builtin_type (type, out_str, hash_state);
> + break;
> + }
> +}
> +
> +/* Get the typeinfo mangled name string for any C type.
> + Returns the mangled type string following Itanium C++ ABI conventions. */
> +
> +std::string
> +typeinfo_get_name (tree type)
> +{
> + gcc_assert (type != NULL_TREE);
> + std::string result = "_ZTS";
> +
> + mangle_type (type, &result, nullptr);
> + return result;
> +}
> +
> +/* Get the typeinfo hash for any C type.
> + Returns the FNV-1a hash of the mangled type string. */
> +
> +uint32_t
> +typeinfo_get_hash (tree type)
> +{
> + gcc_assert (type != NULL_TREE);
> + uint32_t hash_state = 2166136261U; /* FNV-1a 32-bit offset basis. */
> +
> + /* Include _ZTS prefix in hash calculation. */
> + append_string ("_ZTS", nullptr, &hash_state);
> +
> + mangle_type (type, nullptr, &hash_state);
> + return hash_state;
> +}
> --
> 2.34.1
>
Powered by blists - more mailing lists