1*936cf61cSSami Tolvanen // SPDX-License-Identifier: GPL-2.0 2*936cf61cSSami Tolvanen /* 3*936cf61cSSami Tolvanen * Copyright (C) 2024 Google LLC 4*936cf61cSSami Tolvanen */ 5*936cf61cSSami Tolvanen 6*936cf61cSSami Tolvanen #define _GNU_SOURCE 7*936cf61cSSami Tolvanen #include <errno.h> 8*936cf61cSSami Tolvanen #include <stdio.h> 9*936cf61cSSami Tolvanen 10*936cf61cSSami Tolvanen #include "gendwarfksyms.h" 11*936cf61cSSami Tolvanen 12*936cf61cSSami Tolvanen #define KABI_RULE_SECTION ".discard.gendwarfksyms.kabi_rules" 13*936cf61cSSami Tolvanen #define KABI_RULE_VERSION "1" 14*936cf61cSSami Tolvanen 15*936cf61cSSami Tolvanen /* 16*936cf61cSSami Tolvanen * The rule section consists of four null-terminated strings per 17*936cf61cSSami Tolvanen * entry: 18*936cf61cSSami Tolvanen * 19*936cf61cSSami Tolvanen * 1. version 20*936cf61cSSami Tolvanen * Entry format version. Must match KABI_RULE_VERSION. 21*936cf61cSSami Tolvanen * 22*936cf61cSSami Tolvanen * 2. type 23*936cf61cSSami Tolvanen * Type of the kABI rule. Must be one of the tags defined below. 24*936cf61cSSami Tolvanen * 25*936cf61cSSami Tolvanen * 3. target 26*936cf61cSSami Tolvanen * Rule-dependent target, typically the fully qualified name of 27*936cf61cSSami Tolvanen * the target DIE. 28*936cf61cSSami Tolvanen * 29*936cf61cSSami Tolvanen * 4. value 30*936cf61cSSami Tolvanen * Rule-dependent value. 31*936cf61cSSami Tolvanen */ 32*936cf61cSSami Tolvanen #define KABI_RULE_MIN_ENTRY_SIZE \ 33*936cf61cSSami Tolvanen (/* version\0 */ 2 + /* type\0 */ 2 + /* target\0" */ 1 + \ 34*936cf61cSSami Tolvanen /* value\0 */ 1) 35*936cf61cSSami Tolvanen #define KABI_RULE_EMPTY_VALUE "" 36*936cf61cSSami Tolvanen 37*936cf61cSSami Tolvanen /* 38*936cf61cSSami Tolvanen * Rule: declonly 39*936cf61cSSami Tolvanen * - For the struct/enum/union in the target field, treat it as a 40*936cf61cSSami Tolvanen * declaration only even if a definition is available. 41*936cf61cSSami Tolvanen */ 42*936cf61cSSami Tolvanen #define KABI_RULE_TAG_DECLONLY "declonly" 43*936cf61cSSami Tolvanen 44*936cf61cSSami Tolvanen /* 45*936cf61cSSami Tolvanen * Rule: enumerator_ignore 46*936cf61cSSami Tolvanen * - For the enum_field in the target field, ignore the enumerator. 47*936cf61cSSami Tolvanen */ 48*936cf61cSSami Tolvanen #define KABI_RULE_TAG_ENUMERATOR_IGNORE "enumerator_ignore" 49*936cf61cSSami Tolvanen 50*936cf61cSSami Tolvanen /* 51*936cf61cSSami Tolvanen * Rule: enumerator_value 52*936cf61cSSami Tolvanen * - For the fqn_field in the target field, set the value to the 53*936cf61cSSami Tolvanen * unsigned integer in the value field. 54*936cf61cSSami Tolvanen */ 55*936cf61cSSami Tolvanen #define KABI_RULE_TAG_ENUMERATOR_VALUE "enumerator_value" 56*936cf61cSSami Tolvanen 57*936cf61cSSami Tolvanen enum kabi_rule_type { 58*936cf61cSSami Tolvanen KABI_RULE_TYPE_UNKNOWN, 59*936cf61cSSami Tolvanen KABI_RULE_TYPE_DECLONLY, 60*936cf61cSSami Tolvanen KABI_RULE_TYPE_ENUMERATOR_IGNORE, 61*936cf61cSSami Tolvanen KABI_RULE_TYPE_ENUMERATOR_VALUE, 62*936cf61cSSami Tolvanen }; 63*936cf61cSSami Tolvanen 64*936cf61cSSami Tolvanen #define RULE_HASH_BITS 7 65*936cf61cSSami Tolvanen 66*936cf61cSSami Tolvanen struct rule { 67*936cf61cSSami Tolvanen enum kabi_rule_type type; 68*936cf61cSSami Tolvanen const char *target; 69*936cf61cSSami Tolvanen const char *value; 70*936cf61cSSami Tolvanen struct hlist_node hash; 71*936cf61cSSami Tolvanen }; 72*936cf61cSSami Tolvanen 73*936cf61cSSami Tolvanen /* { type, target } -> struct rule */ 74*936cf61cSSami Tolvanen static HASHTABLE_DEFINE(rules, 1 << RULE_HASH_BITS); 75*936cf61cSSami Tolvanen 76*936cf61cSSami Tolvanen static inline unsigned int rule_values_hash(enum kabi_rule_type type, 77*936cf61cSSami Tolvanen const char *target) 78*936cf61cSSami Tolvanen { 79*936cf61cSSami Tolvanen return hash_32(type) ^ hash_str(target); 80*936cf61cSSami Tolvanen } 81*936cf61cSSami Tolvanen 82*936cf61cSSami Tolvanen static inline unsigned int rule_hash(const struct rule *rule) 83*936cf61cSSami Tolvanen { 84*936cf61cSSami Tolvanen return rule_values_hash(rule->type, rule->target); 85*936cf61cSSami Tolvanen } 86*936cf61cSSami Tolvanen 87*936cf61cSSami Tolvanen static inline const char *get_rule_field(const char **pos, ssize_t *left) 88*936cf61cSSami Tolvanen { 89*936cf61cSSami Tolvanen const char *start = *pos; 90*936cf61cSSami Tolvanen size_t len; 91*936cf61cSSami Tolvanen 92*936cf61cSSami Tolvanen if (*left <= 0) 93*936cf61cSSami Tolvanen error("unexpected end of kABI rules"); 94*936cf61cSSami Tolvanen 95*936cf61cSSami Tolvanen len = strnlen(start, *left) + 1; 96*936cf61cSSami Tolvanen *pos += len; 97*936cf61cSSami Tolvanen *left -= len; 98*936cf61cSSami Tolvanen 99*936cf61cSSami Tolvanen return start; 100*936cf61cSSami Tolvanen } 101*936cf61cSSami Tolvanen 102*936cf61cSSami Tolvanen void kabi_read_rules(int fd) 103*936cf61cSSami Tolvanen { 104*936cf61cSSami Tolvanen GElf_Shdr shdr_mem; 105*936cf61cSSami Tolvanen GElf_Shdr *shdr; 106*936cf61cSSami Tolvanen Elf_Data *rule_data = NULL; 107*936cf61cSSami Tolvanen Elf_Scn *scn; 108*936cf61cSSami Tolvanen Elf *elf; 109*936cf61cSSami Tolvanen size_t shstrndx; 110*936cf61cSSami Tolvanen const char *rule_str; 111*936cf61cSSami Tolvanen ssize_t left; 112*936cf61cSSami Tolvanen int i; 113*936cf61cSSami Tolvanen 114*936cf61cSSami Tolvanen const struct { 115*936cf61cSSami Tolvanen enum kabi_rule_type type; 116*936cf61cSSami Tolvanen const char *tag; 117*936cf61cSSami Tolvanen } rule_types[] = { 118*936cf61cSSami Tolvanen { 119*936cf61cSSami Tolvanen .type = KABI_RULE_TYPE_DECLONLY, 120*936cf61cSSami Tolvanen .tag = KABI_RULE_TAG_DECLONLY, 121*936cf61cSSami Tolvanen }, 122*936cf61cSSami Tolvanen { 123*936cf61cSSami Tolvanen .type = KABI_RULE_TYPE_ENUMERATOR_IGNORE, 124*936cf61cSSami Tolvanen .tag = KABI_RULE_TAG_ENUMERATOR_IGNORE, 125*936cf61cSSami Tolvanen }, 126*936cf61cSSami Tolvanen { 127*936cf61cSSami Tolvanen .type = KABI_RULE_TYPE_ENUMERATOR_VALUE, 128*936cf61cSSami Tolvanen .tag = KABI_RULE_TAG_ENUMERATOR_VALUE, 129*936cf61cSSami Tolvanen }, 130*936cf61cSSami Tolvanen }; 131*936cf61cSSami Tolvanen 132*936cf61cSSami Tolvanen if (!stable) 133*936cf61cSSami Tolvanen return; 134*936cf61cSSami Tolvanen 135*936cf61cSSami Tolvanen if (elf_version(EV_CURRENT) != EV_CURRENT) 136*936cf61cSSami Tolvanen error("elf_version failed: %s", elf_errmsg(-1)); 137*936cf61cSSami Tolvanen 138*936cf61cSSami Tolvanen elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); 139*936cf61cSSami Tolvanen if (!elf) 140*936cf61cSSami Tolvanen error("elf_begin failed: %s", elf_errmsg(-1)); 141*936cf61cSSami Tolvanen 142*936cf61cSSami Tolvanen if (elf_getshdrstrndx(elf, &shstrndx) < 0) 143*936cf61cSSami Tolvanen error("elf_getshdrstrndx failed: %s", elf_errmsg(-1)); 144*936cf61cSSami Tolvanen 145*936cf61cSSami Tolvanen scn = elf_nextscn(elf, NULL); 146*936cf61cSSami Tolvanen 147*936cf61cSSami Tolvanen while (scn) { 148*936cf61cSSami Tolvanen const char *sname; 149*936cf61cSSami Tolvanen 150*936cf61cSSami Tolvanen shdr = gelf_getshdr(scn, &shdr_mem); 151*936cf61cSSami Tolvanen if (!shdr) 152*936cf61cSSami Tolvanen error("gelf_getshdr failed: %s", elf_errmsg(-1)); 153*936cf61cSSami Tolvanen 154*936cf61cSSami Tolvanen sname = elf_strptr(elf, shstrndx, shdr->sh_name); 155*936cf61cSSami Tolvanen if (!sname) 156*936cf61cSSami Tolvanen error("elf_strptr failed: %s", elf_errmsg(-1)); 157*936cf61cSSami Tolvanen 158*936cf61cSSami Tolvanen if (!strcmp(sname, KABI_RULE_SECTION)) { 159*936cf61cSSami Tolvanen rule_data = elf_getdata(scn, NULL); 160*936cf61cSSami Tolvanen if (!rule_data) 161*936cf61cSSami Tolvanen error("elf_getdata failed: %s", elf_errmsg(-1)); 162*936cf61cSSami Tolvanen break; 163*936cf61cSSami Tolvanen } 164*936cf61cSSami Tolvanen 165*936cf61cSSami Tolvanen scn = elf_nextscn(elf, scn); 166*936cf61cSSami Tolvanen } 167*936cf61cSSami Tolvanen 168*936cf61cSSami Tolvanen if (!rule_data) { 169*936cf61cSSami Tolvanen debug("kABI rules not found"); 170*936cf61cSSami Tolvanen check(elf_end(elf)); 171*936cf61cSSami Tolvanen return; 172*936cf61cSSami Tolvanen } 173*936cf61cSSami Tolvanen 174*936cf61cSSami Tolvanen rule_str = rule_data->d_buf; 175*936cf61cSSami Tolvanen left = shdr->sh_size; 176*936cf61cSSami Tolvanen 177*936cf61cSSami Tolvanen if (left < KABI_RULE_MIN_ENTRY_SIZE) 178*936cf61cSSami Tolvanen error("kABI rule section too small: %zd bytes", left); 179*936cf61cSSami Tolvanen 180*936cf61cSSami Tolvanen if (rule_str[left - 1] != '\0') 181*936cf61cSSami Tolvanen error("kABI rules are not null-terminated"); 182*936cf61cSSami Tolvanen 183*936cf61cSSami Tolvanen while (left > KABI_RULE_MIN_ENTRY_SIZE) { 184*936cf61cSSami Tolvanen enum kabi_rule_type type = KABI_RULE_TYPE_UNKNOWN; 185*936cf61cSSami Tolvanen const char *field; 186*936cf61cSSami Tolvanen struct rule *rule; 187*936cf61cSSami Tolvanen 188*936cf61cSSami Tolvanen /* version */ 189*936cf61cSSami Tolvanen field = get_rule_field(&rule_str, &left); 190*936cf61cSSami Tolvanen 191*936cf61cSSami Tolvanen if (strcmp(field, KABI_RULE_VERSION)) 192*936cf61cSSami Tolvanen error("unsupported kABI rule version: '%s'", field); 193*936cf61cSSami Tolvanen 194*936cf61cSSami Tolvanen /* type */ 195*936cf61cSSami Tolvanen field = get_rule_field(&rule_str, &left); 196*936cf61cSSami Tolvanen 197*936cf61cSSami Tolvanen for (i = 0; i < ARRAY_SIZE(rule_types); i++) { 198*936cf61cSSami Tolvanen if (!strcmp(field, rule_types[i].tag)) { 199*936cf61cSSami Tolvanen type = rule_types[i].type; 200*936cf61cSSami Tolvanen break; 201*936cf61cSSami Tolvanen } 202*936cf61cSSami Tolvanen } 203*936cf61cSSami Tolvanen 204*936cf61cSSami Tolvanen if (type == KABI_RULE_TYPE_UNKNOWN) 205*936cf61cSSami Tolvanen error("unsupported kABI rule type: '%s'", field); 206*936cf61cSSami Tolvanen 207*936cf61cSSami Tolvanen rule = xmalloc(sizeof(struct rule)); 208*936cf61cSSami Tolvanen 209*936cf61cSSami Tolvanen rule->type = type; 210*936cf61cSSami Tolvanen rule->target = xstrdup(get_rule_field(&rule_str, &left)); 211*936cf61cSSami Tolvanen rule->value = xstrdup(get_rule_field(&rule_str, &left)); 212*936cf61cSSami Tolvanen 213*936cf61cSSami Tolvanen hash_add(rules, &rule->hash, rule_hash(rule)); 214*936cf61cSSami Tolvanen 215*936cf61cSSami Tolvanen debug("kABI rule: type: '%s', target: '%s', value: '%s'", field, 216*936cf61cSSami Tolvanen rule->target, rule->value); 217*936cf61cSSami Tolvanen } 218*936cf61cSSami Tolvanen 219*936cf61cSSami Tolvanen if (left > 0) 220*936cf61cSSami Tolvanen warn("unexpected data at the end of the kABI rules section"); 221*936cf61cSSami Tolvanen 222*936cf61cSSami Tolvanen check(elf_end(elf)); 223*936cf61cSSami Tolvanen } 224*936cf61cSSami Tolvanen 225*936cf61cSSami Tolvanen bool kabi_is_declonly(const char *fqn) 226*936cf61cSSami Tolvanen { 227*936cf61cSSami Tolvanen struct rule *rule; 228*936cf61cSSami Tolvanen 229*936cf61cSSami Tolvanen if (!stable) 230*936cf61cSSami Tolvanen return false; 231*936cf61cSSami Tolvanen if (!fqn || !*fqn) 232*936cf61cSSami Tolvanen return false; 233*936cf61cSSami Tolvanen 234*936cf61cSSami Tolvanen hash_for_each_possible(rules, rule, hash, 235*936cf61cSSami Tolvanen rule_values_hash(KABI_RULE_TYPE_DECLONLY, fqn)) { 236*936cf61cSSami Tolvanen if (rule->type == KABI_RULE_TYPE_DECLONLY && 237*936cf61cSSami Tolvanen !strcmp(fqn, rule->target)) 238*936cf61cSSami Tolvanen return true; 239*936cf61cSSami Tolvanen } 240*936cf61cSSami Tolvanen 241*936cf61cSSami Tolvanen return false; 242*936cf61cSSami Tolvanen } 243*936cf61cSSami Tolvanen 244*936cf61cSSami Tolvanen static char *get_enumerator_target(const char *fqn, const char *field) 245*936cf61cSSami Tolvanen { 246*936cf61cSSami Tolvanen char *target = NULL; 247*936cf61cSSami Tolvanen 248*936cf61cSSami Tolvanen if (asprintf(&target, "%s %s", fqn, field) < 0) 249*936cf61cSSami Tolvanen error("asprintf failed for '%s %s'", fqn, field); 250*936cf61cSSami Tolvanen 251*936cf61cSSami Tolvanen return target; 252*936cf61cSSami Tolvanen } 253*936cf61cSSami Tolvanen 254*936cf61cSSami Tolvanen static unsigned long get_ulong_value(const char *value) 255*936cf61cSSami Tolvanen { 256*936cf61cSSami Tolvanen unsigned long result = 0; 257*936cf61cSSami Tolvanen char *endptr = NULL; 258*936cf61cSSami Tolvanen 259*936cf61cSSami Tolvanen errno = 0; 260*936cf61cSSami Tolvanen result = strtoul(value, &endptr, 10); 261*936cf61cSSami Tolvanen 262*936cf61cSSami Tolvanen if (errno || *endptr) 263*936cf61cSSami Tolvanen error("invalid unsigned value '%s'", value); 264*936cf61cSSami Tolvanen 265*936cf61cSSami Tolvanen return result; 266*936cf61cSSami Tolvanen } 267*936cf61cSSami Tolvanen 268*936cf61cSSami Tolvanen bool kabi_is_enumerator_ignored(const char *fqn, const char *field) 269*936cf61cSSami Tolvanen { 270*936cf61cSSami Tolvanen bool match = false; 271*936cf61cSSami Tolvanen struct rule *rule; 272*936cf61cSSami Tolvanen char *target; 273*936cf61cSSami Tolvanen 274*936cf61cSSami Tolvanen if (!stable) 275*936cf61cSSami Tolvanen return false; 276*936cf61cSSami Tolvanen if (!fqn || !*fqn || !field || !*field) 277*936cf61cSSami Tolvanen return false; 278*936cf61cSSami Tolvanen 279*936cf61cSSami Tolvanen target = get_enumerator_target(fqn, field); 280*936cf61cSSami Tolvanen 281*936cf61cSSami Tolvanen hash_for_each_possible( 282*936cf61cSSami Tolvanen rules, rule, hash, 283*936cf61cSSami Tolvanen rule_values_hash(KABI_RULE_TYPE_ENUMERATOR_IGNORE, target)) { 284*936cf61cSSami Tolvanen if (rule->type == KABI_RULE_TYPE_ENUMERATOR_IGNORE && 285*936cf61cSSami Tolvanen !strcmp(target, rule->target)) { 286*936cf61cSSami Tolvanen match = true; 287*936cf61cSSami Tolvanen break; 288*936cf61cSSami Tolvanen } 289*936cf61cSSami Tolvanen } 290*936cf61cSSami Tolvanen 291*936cf61cSSami Tolvanen free(target); 292*936cf61cSSami Tolvanen return match; 293*936cf61cSSami Tolvanen } 294*936cf61cSSami Tolvanen 295*936cf61cSSami Tolvanen bool kabi_get_enumerator_value(const char *fqn, const char *field, 296*936cf61cSSami Tolvanen unsigned long *value) 297*936cf61cSSami Tolvanen { 298*936cf61cSSami Tolvanen bool match = false; 299*936cf61cSSami Tolvanen struct rule *rule; 300*936cf61cSSami Tolvanen char *target; 301*936cf61cSSami Tolvanen 302*936cf61cSSami Tolvanen if (!stable) 303*936cf61cSSami Tolvanen return false; 304*936cf61cSSami Tolvanen if (!fqn || !*fqn || !field || !*field) 305*936cf61cSSami Tolvanen return false; 306*936cf61cSSami Tolvanen 307*936cf61cSSami Tolvanen target = get_enumerator_target(fqn, field); 308*936cf61cSSami Tolvanen 309*936cf61cSSami Tolvanen hash_for_each_possible(rules, rule, hash, 310*936cf61cSSami Tolvanen rule_values_hash(KABI_RULE_TYPE_ENUMERATOR_VALUE, 311*936cf61cSSami Tolvanen target)) { 312*936cf61cSSami Tolvanen if (rule->type == KABI_RULE_TYPE_ENUMERATOR_VALUE && 313*936cf61cSSami Tolvanen !strcmp(target, rule->target)) { 314*936cf61cSSami Tolvanen *value = get_ulong_value(rule->value); 315*936cf61cSSami Tolvanen match = true; 316*936cf61cSSami Tolvanen break; 317*936cf61cSSami Tolvanen } 318*936cf61cSSami Tolvanen } 319*936cf61cSSami Tolvanen 320*936cf61cSSami Tolvanen free(target); 321*936cf61cSSami Tolvanen return match; 322*936cf61cSSami Tolvanen } 323*936cf61cSSami Tolvanen 324*936cf61cSSami Tolvanen void kabi_free(void) 325*936cf61cSSami Tolvanen { 326*936cf61cSSami Tolvanen struct hlist_node *tmp; 327*936cf61cSSami Tolvanen struct rule *rule; 328*936cf61cSSami Tolvanen 329*936cf61cSSami Tolvanen hash_for_each_safe(rules, rule, tmp, hash) { 330*936cf61cSSami Tolvanen free((void *)rule->target); 331*936cf61cSSami Tolvanen free((void *)rule->value); 332*936cf61cSSami Tolvanen free(rule); 333*936cf61cSSami Tolvanen } 334*936cf61cSSami Tolvanen 335*936cf61cSSami Tolvanen hash_init(rules); 336*936cf61cSSami Tolvanen } 337