case BTF_KIND_TYPEDEF:
        case BTF_KIND_FUNC:
        case BTF_KIND_FLOAT:
+       case BTF_KIND_TYPE_TAG:
                return base_size;
        case BTF_KIND_INT:
                return base_size + sizeof(__u32);
        case BTF_KIND_TYPEDEF:
        case BTF_KIND_FUNC:
        case BTF_KIND_FLOAT:
+       case BTF_KIND_TYPE_TAG:
                return 0;
        case BTF_KIND_INT:
                *(__u32 *)(t + 1) = bswap_32(*(__u32 *)(t + 1));
        case BTF_KIND_VOLATILE:
        case BTF_KIND_CONST:
        case BTF_KIND_RESTRICT:
+       case BTF_KIND_TYPE_TAG:
                return btf__align_of(btf, t->type);
        case BTF_KIND_ARRAY:
                return btf__align_of(btf, btf_array(t)->type);
        return btf_add_ref_kind(btf, BTF_KIND_RESTRICT, NULL, ref_type_id);
 }
 
+/*
+ * Append new BTF_KIND_TYPE_TAG type with:
+ *   - *value*, non-empty/non-NULL tag value;
+ *   - *ref_type_id* - referenced type ID, it might not exist yet;
+ * Returns:
+ *   - >0, type ID of newly added BTF type;
+ *   - <0, on error.
+ */
+int btf__add_type_tag(struct btf *btf, const char *value, int ref_type_id)
+{
+       if (!value|| !value[0])
+               return libbpf_err(-EINVAL);
+
+       return btf_add_ref_kind(btf, BTF_KIND_TYPE_TAG, value, ref_type_id);
+}
+
 /*
  * Append new BTF_KIND_FUNC type with:
  *   - *name*, non-empty/non-NULL name;
                case BTF_KIND_TYPEDEF:
                case BTF_KIND_FUNC:
                case BTF_KIND_FLOAT:
+               case BTF_KIND_TYPE_TAG:
                        h = btf_hash_common(t);
                        break;
                case BTF_KIND_INT:
        case BTF_KIND_VAR:
        case BTF_KIND_DATASEC:
        case BTF_KIND_DECL_TAG:
+       case BTF_KIND_TYPE_TAG:
                return 0;
 
        case BTF_KIND_INT:
        case BTF_KIND_PTR:
        case BTF_KIND_TYPEDEF:
        case BTF_KIND_FUNC:
+       case BTF_KIND_TYPE_TAG:
                ref_type_id = btf_dedup_ref_type(d, t->type);
                if (ref_type_id < 0)
                        return ref_type_id;
        case BTF_KIND_FUNC:
        case BTF_KIND_VAR:
        case BTF_KIND_DECL_TAG:
+       case BTF_KIND_TYPE_TAG:
                return visit(&t->type, ctx);
 
        case BTF_KIND_ARRAY: {
 
 LIBBPF_API int btf__add_volatile(struct btf *btf, int ref_type_id);
 LIBBPF_API int btf__add_const(struct btf *btf, int ref_type_id);
 LIBBPF_API int btf__add_restrict(struct btf *btf, int ref_type_id);
+LIBBPF_API int btf__add_type_tag(struct btf *btf, const char *value, int ref_type_id);
 
 /* func and func_proto construction APIs */
 LIBBPF_API int btf__add_func(struct btf *btf, const char *name,
 
        return kind == BTF_KIND_VOLATILE ||
               kind == BTF_KIND_CONST ||
-              kind == BTF_KIND_RESTRICT;
+              kind == BTF_KIND_RESTRICT ||
+              kind == BTF_KIND_TYPE_TAG;
 }
 
 static inline bool btf_is_func(const struct btf_type *t)
        return btf_kind(t) == BTF_KIND_DECL_TAG;
 }
 
+static inline bool btf_is_type_tag(const struct btf_type *t)
+{
+       return btf_kind(t) == BTF_KIND_TYPE_TAG;
+}
+
 static inline __u8 btf_int_encoding(const struct btf_type *t)
 {
        return BTF_INT_ENCODING(*(__u32 *)(t + 1));
 
                case BTF_KIND_FUNC:
                case BTF_KIND_VAR:
                case BTF_KIND_DECL_TAG:
+               case BTF_KIND_TYPE_TAG:
                        d->type_states[t->type].referenced = 1;
                        break;
 
        case BTF_KIND_VOLATILE:
        case BTF_KIND_CONST:
        case BTF_KIND_RESTRICT:
+       case BTF_KIND_TYPE_TAG:
                return btf_dump_order_type(d, t->type, through_ptr);
 
        case BTF_KIND_FUNC_PROTO: {
        case BTF_KIND_VOLATILE:
        case BTF_KIND_CONST:
        case BTF_KIND_RESTRICT:
+       case BTF_KIND_TYPE_TAG:
                btf_dump_emit_type(d, t->type, cont_id);
                break;
        case BTF_KIND_ARRAY:
                case BTF_KIND_CONST:
                case BTF_KIND_RESTRICT:
                case BTF_KIND_FUNC_PROTO:
+               case BTF_KIND_TYPE_TAG:
                        id = t->type;
                        break;
                case BTF_KIND_ARRAY:
                case BTF_KIND_RESTRICT:
                        btf_dump_printf(d, " restrict");
                        break;
+               case BTF_KIND_TYPE_TAG:
+                       btf_dump_emit_mods(d, decls);
+                       name = btf_name_of(d, t->name_off);
+                       btf_dump_printf(d, " __attribute__((btf_type_tag(\"%s\")))", name);
+                       break;
                case BTF_KIND_ARRAY: {
                        const struct btf_array *a = btf_array(t);
                        const struct btf_type *next_t;
 
        FEAT_PERF_LINK,
        /* BTF_KIND_DECL_TAG support */
        FEAT_BTF_DECL_TAG,
+       /* BTF_KIND_TYPE_TAG support */
+       FEAT_BTF_TYPE_TAG,
        __FEAT_CNT,
 };
 
        case BTF_KIND_DATASEC: return "datasec";
        case BTF_KIND_FLOAT: return "float";
        case BTF_KIND_DECL_TAG: return "decl_tag";
+       case BTF_KIND_TYPE_TAG: return "type_tag";
        default: return "unknown";
        }
 }
        bool has_float = kernel_supports(obj, FEAT_BTF_FLOAT);
        bool has_func = kernel_supports(obj, FEAT_BTF_FUNC);
        bool has_decl_tag = kernel_supports(obj, FEAT_BTF_DECL_TAG);
+       bool has_type_tag = kernel_supports(obj, FEAT_BTF_TYPE_TAG);
 
-       return !has_func || !has_datasec || !has_func_global || !has_float || !has_decl_tag;
+       return !has_func || !has_datasec || !has_func_global || !has_float ||
+              !has_decl_tag || !has_type_tag;
 }
 
 static void bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf)
        bool has_float = kernel_supports(obj, FEAT_BTF_FLOAT);
        bool has_func = kernel_supports(obj, FEAT_BTF_FUNC);
        bool has_decl_tag = kernel_supports(obj, FEAT_BTF_DECL_TAG);
+       bool has_type_tag = kernel_supports(obj, FEAT_BTF_TYPE_TAG);
        struct btf_type *t;
        int i, j, vlen;
 
                         */
                        t->name_off = 0;
                        t->info = BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 0);
+               } else if (!has_type_tag && btf_is_type_tag(t)) {
+                       /* replace TYPE_TAG with a CONST */
+                       t->name_off = 0;
+                       t->info = BTF_INFO_ENC(BTF_KIND_CONST, 0, 0);
                }
        }
 }
                                             strs, sizeof(strs)));
 }
 
+static int probe_kern_btf_type_tag(void)
+{
+       static const char strs[] = "\0tag";
+       __u32 types[] = {
+               /* int */
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
+               /* attr */
+               BTF_TYPE_TYPE_TAG_ENC(1, 1),                            /* [2] */
+               /* ptr */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_PTR, 0, 0), 2),   /* [3] */
+       };
+
+       return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
+                                            strs, sizeof(strs)));
+}
+
 static int probe_kern_array_mmap(void)
 {
        struct bpf_create_map_attr attr = {
        [FEAT_BTF_DECL_TAG] = {
                "BTF_KIND_DECL_TAG support", probe_kern_btf_decl_tag,
        },
+       [FEAT_BTF_TYPE_TAG] = {
+               "BTF_KIND_TYPE_TAG support", probe_kern_btf_type_tag,
+       },
 };
 
 static bool kernel_supports(const struct bpf_object *obj, enum kern_feature_id feat_id)
 
                bpf_program__set_extra_flags;
                btf__add_btf;
                btf__add_decl_tag;
+               btf__add_type_tag;
                btf__dedup;
                btf__dedup_deprecated;
                btf__raw_data;
 
        BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_FLOAT, 0, 0), sz)
 #define BTF_TYPE_DECL_TAG_ENC(value, type, component_idx) \
        BTF_TYPE_ENC(value, BTF_INFO_ENC(BTF_KIND_DECL_TAG, 0, 0), type), (component_idx)
+#define BTF_TYPE_TYPE_TAG_ENC(value, type) \
+       BTF_TYPE_ENC(value, BTF_INFO_ENC(BTF_KIND_TYPE_TAG, 0, 0), type)
 
 #ifndef likely
 #define likely(x) __builtin_expect(!!(x), 1)