Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gendwarfksyms: Add support for kABI rules

Distributions that want to maintain a stable kABI need the ability
to make ABI compatible changes to kernel without affecting symbol
versions, either because of LTS updates or backports.

With genksyms, developers would typically hide these changes from
version calculation with #ifndef __GENKSYMS__, which would result
in the symbol version not changing even though the actual type has
changed. When we process precompiled object files, this isn't an
option.

To support this use case, add a --stable command line flag that
gates kABI stability features that are not needed in mainline
kernels, but can be useful for distributions, and add support for
kABI rules, which can be used to restrict gendwarfksyms output.

The rules are specified as a set of null-terminated strings stored
in the .discard.gendwarfksyms.kabi_rules section. Each rule consists
of four strings as follows:

"version\0type\0target\0value"

The version string ensures the structure can be changed in a
backwards compatible way. The type string indicates the type of the
rule, and target and value strings contain rule-specific data.

Initially support two simple rules:

1. Declaration-only types

A type declaration can change into a full definition when
additional includes are pulled in to the TU, which changes the
versions of any symbol that references the type. Add support
for defining declaration-only types whose definition is not
expanded during versioning.

2. Ignored enumerators

It's possible to add new enum fields without changing the ABI,
but as the fields are included in symbol versioning, this would
change the versions. Add support for ignoring specific fields.

3. Overridden enumerator values

Add support for overriding enumerator values when calculating
versions. This may be needed when the last field of the enum
is used as a sentinel and new fields must be added before it.

Add examples for using the rules under the examples/ directory.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>

authored by

Sami Tolvanen and committed by
Masahiro Yamada
936cf61c 71378888

+531 -4
+1
scripts/gendwarfksyms/Makefile
··· 5 5 gendwarfksyms-objs += cache.o 6 6 gendwarfksyms-objs += die.o 7 7 gendwarfksyms-objs += dwarf.o 8 + gendwarfksyms-objs += kabi.o 8 9 gendwarfksyms-objs += symbols.o 9 10 gendwarfksyms-objs += types.o 10 11
+22 -3
scripts/gendwarfksyms/dwarf.c
··· 120 120 return !!res; 121 121 } 122 122 123 - static bool is_kabi_definition(Dwarf_Die *die) 123 + static bool is_kabi_definition(struct die *cache, Dwarf_Die *die) 124 124 { 125 125 bool value; 126 126 127 127 if (get_flag_attr(die, DW_AT_declaration, &value) && value) 128 + return false; 129 + 130 + if (kabi_is_declonly(cache->fqn)) 128 131 return false; 129 132 130 133 return !is_definition_private(die); ··· 518 515 process(cache, " {"); 519 516 process_linebreak(cache, 1); 520 517 521 - expand = state->expand.expand && is_kabi_definition(die); 518 + expand = state->expand.expand && is_kabi_definition(cache, die); 522 519 523 520 if (expand) { 521 + state->expand.current_fqn = cache->fqn; 524 522 check(process_die_container(state, cache, die, process_func, 525 523 match_func)); 526 524 } ··· 552 548 static void process_enumerator_type(struct state *state, struct die *cache, 553 549 Dwarf_Die *die) 554 550 { 551 + bool overridden = false; 555 552 Dwarf_Word value; 553 + 554 + if (stable) { 555 + /* Get the fqn before we process anything */ 556 + update_fqn(cache, die); 557 + 558 + if (kabi_is_enumerator_ignored(state->expand.current_fqn, 559 + cache->fqn)) 560 + return; 561 + 562 + overridden = kabi_get_enumerator_value( 563 + state->expand.current_fqn, cache->fqn, &value); 564 + } 556 565 557 566 process_list_comma(state, cache); 558 567 process(cache, "enumerator"); 559 568 process_fqn(cache, die); 560 569 561 - if (get_udata_attr(die, DW_AT_const_value, &value)) { 570 + if (overridden || get_udata_attr(die, DW_AT_const_value, &value)) { 562 571 process(cache, " = "); 563 572 process_fmt(cache, "%" PRIu64, value); 564 573 } ··· 637 620 static void state_init(struct state *state) 638 621 { 639 622 state->expand.expand = true; 623 + state->expand.current_fqn = NULL; 640 624 cache_init(&state->expansion_cache); 641 625 } 642 626 ··· 645 627 struct expansion_state *saved) 646 628 { 647 629 state->expand = saved->expand; 630 + state->current_fqn = saved->current_fqn; 648 631 } 649 632 650 633 static void expansion_state_save(struct expansion_state *state,
+70
scripts/gendwarfksyms/examples/kabi.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2024 Google LLC 4 + * 5 + * Example macros for maintaining kABI stability. 6 + * 7 + * This file is based on android_kabi.h, which has the following notice: 8 + * 9 + * Heavily influenced by rh_kabi.h which came from the RHEL/CENTOS kernel 10 + * and was: 11 + * Copyright (c) 2014 Don Zickus 12 + * Copyright (c) 2015-2018 Jiri Benc 13 + * Copyright (c) 2015 Sabrina Dubroca, Hannes Frederic Sowa 14 + * Copyright (c) 2016-2018 Prarit Bhargava 15 + * Copyright (c) 2017 Paolo Abeni, Larry Woodman 16 + */ 17 + 18 + #ifndef __KABI_H__ 19 + #define __KABI_H__ 20 + 21 + /* Kernel macros for userspace testing. */ 22 + #ifndef __aligned 23 + #define __aligned(x) __attribute__((__aligned__(x))) 24 + #endif 25 + #ifndef __used 26 + #define __used __attribute__((__used__)) 27 + #endif 28 + #ifndef __section 29 + #define __section(section) __attribute__((__section__(section))) 30 + #endif 31 + #ifndef __PASTE 32 + #define ___PASTE(a, b) a##b 33 + #define __PASTE(a, b) ___PASTE(a, b) 34 + #endif 35 + #ifndef __stringify 36 + #define __stringify_1(x...) #x 37 + #define __stringify(x...) __stringify_1(x) 38 + #endif 39 + 40 + #define __KABI_RULE(hint, target, value) \ 41 + static const char __PASTE(__gendwarfksyms_rule_, \ 42 + __COUNTER__)[] __used __aligned(1) \ 43 + __section(".discard.gendwarfksyms.kabi_rules") = \ 44 + "1\0" #hint "\0" #target "\0" #value 45 + 46 + /* 47 + * KABI_DECLONLY(fqn) 48 + * Treat the struct/union/enum fqn as a declaration, i.e. even if 49 + * a definition is available, don't expand the contents. 50 + */ 51 + #define KABI_DECLONLY(fqn) __KABI_RULE(declonly, fqn, ) 52 + 53 + /* 54 + * KABI_ENUMERATOR_IGNORE(fqn, field) 55 + * When expanding enum fqn, skip the provided field. This makes it 56 + * possible to hide added enum fields from versioning. 57 + */ 58 + #define KABI_ENUMERATOR_IGNORE(fqn, field) \ 59 + __KABI_RULE(enumerator_ignore, fqn field, ) 60 + 61 + /* 62 + * KABI_ENUMERATOR_VALUE(fqn, field, value) 63 + * When expanding enum fqn, use the provided value for the 64 + * specified field. This makes it possible to override enumerator 65 + * values when calculating versions. 66 + */ 67 + #define KABI_ENUMERATOR_VALUE(fqn, field, value) \ 68 + __KABI_RULE(enumerator_value, fqn field, value) 69 + 70 + #endif /* __KABI_H__ */
+14
scripts/gendwarfksyms/examples/kabi_ex.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * kabi_ex.c 4 + * 5 + * Copyright (C) 2024 Google LLC 6 + * 7 + * Examples for kABI stability features with --stable. See kabi_ex.h 8 + * for details. 9 + */ 10 + 11 + #include "kabi_ex.h" 12 + 13 + struct s e0; 14 + enum e e1;
+64
scripts/gendwarfksyms/examples/kabi_ex.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * kabi_ex.h 4 + * 5 + * Copyright (C) 2024 Google LLC 6 + * 7 + * Examples for kABI stability features with --stable. 8 + */ 9 + 10 + /* 11 + * The comments below each example contain the expected gendwarfksyms 12 + * output, which can be verified using LLVM's FileCheck tool: 13 + * 14 + * https://llvm.org/docs/CommandGuide/FileCheck.html 15 + * 16 + * Usage: 17 + * 18 + * $ gcc -g -c examples/kabi_ex.c -o examples/kabi_ex.o 19 + * 20 + * $ nm examples/kabi_ex.o | awk '{ print $NF }' | \ 21 + * ./gendwarfksyms --stable --dump-dies \ 22 + * examples/kabi_ex.o 2>&1 >/dev/null | \ 23 + * FileCheck examples/kabi_ex.h --check-prefix=STABLE 24 + */ 25 + 26 + #ifndef __KABI_EX_H__ 27 + #define __KABI_EX_H__ 28 + 29 + #include "kabi.h" 30 + 31 + /* 32 + * Example: kABI rules 33 + */ 34 + 35 + struct s { 36 + int a; 37 + }; 38 + 39 + KABI_DECLONLY(s); 40 + 41 + /* 42 + * STABLE: variable structure_type s { 43 + * STABLE-NEXT: } 44 + */ 45 + 46 + enum e { 47 + A, 48 + B, 49 + C, 50 + D, 51 + }; 52 + 53 + KABI_ENUMERATOR_IGNORE(e, B); 54 + KABI_ENUMERATOR_IGNORE(e, C); 55 + KABI_ENUMERATOR_VALUE(e, D, 123456789); 56 + 57 + /* 58 + * STABLE: variable enumeration_type e { 59 + * STABLE-NEXT: enumerator A = 0 , 60 + * STABLE-NEXT: enumerator D = 123456789 61 + * STABLE-NEXT: } byte_size(4) 62 + */ 63 + 64 + #endif /* __KABI_EX_H__ */
+10 -1
scripts/gendwarfksyms/gendwarfksyms.c
··· 25 25 int dump_types; 26 26 /* Print out expanded type strings used for symbol versions */ 27 27 int dump_versions; 28 + /* Support kABI stability features */ 29 + int stable; 28 30 /* Write a symtypes file */ 29 31 int symtypes; 30 32 static const char *symtypes_file; ··· 40 38 " --dump-die-map Print debugging information about die_map changes\n" 41 39 " --dump-types Dump type strings\n" 42 40 " --dump-versions Dump expanded type strings used for symbol versions\n" 41 + " -s, --stable Support kABI stability features\n" 43 42 " -T, --symtypes file Write a symtypes file\n" 44 43 " -h, --help Print this message\n" 45 44 "\n", ··· 101 98 { "dump-die-map", 0, &dump_die_map, 1 }, 102 99 { "dump-types", 0, &dump_types, 1 }, 103 100 { "dump-versions", 0, &dump_versions, 1 }, 101 + { "stable", 0, NULL, 's' }, 104 102 { "symtypes", 1, NULL, 'T' }, 105 103 { "help", 0, NULL, 'h' }, 106 104 { 0, 0, NULL, 0 } 107 105 }; 108 106 109 - while ((opt = getopt_long(argc, argv, "dT:h", opts, NULL)) != EOF) { 107 + while ((opt = getopt_long(argc, argv, "dsT:h", opts, NULL)) != EOF) { 110 108 switch (opt) { 111 109 case 0: 112 110 break; 113 111 case 'd': 114 112 debug = 1; 113 + break; 114 + case 's': 115 + stable = 1; 115 116 break; 116 117 case 'T': 117 118 symtypes = 1; ··· 157 150 strerror(errno)); 158 151 159 152 symbol_read_symtab(fd); 153 + kabi_read_rules(fd); 160 154 161 155 dwfl = dwfl_begin(&callbacks); 162 156 if (!dwfl) ··· 174 166 error("dwfl_getmodules failed for '%s'", argv[n]); 175 167 176 168 dwfl_end(dwfl); 169 + kabi_free(); 177 170 } 178 171 179 172 if (symfile)
+14
scripts/gendwarfksyms/gendwarfksyms.h
··· 24 24 extern int dump_die_map; 25 25 extern int dump_types; 26 26 extern int dump_versions; 27 + extern int stable; 27 28 extern int symtypes; 28 29 29 30 /* ··· 233 232 234 233 struct expansion_state { 235 234 bool expand; 235 + const char *current_fqn; 236 236 }; 237 237 238 238 struct state { ··· 264 262 */ 265 263 266 264 void generate_symtypes_and_versions(FILE *file); 265 + 266 + /* 267 + * kabi.c 268 + */ 269 + 270 + bool kabi_is_enumerator_ignored(const char *fqn, const char *field); 271 + bool kabi_get_enumerator_value(const char *fqn, const char *field, 272 + unsigned long *value); 273 + bool kabi_is_declonly(const char *fqn); 274 + 275 + void kabi_read_rules(int fd); 276 + void kabi_free(void); 267 277 268 278 #endif /* __GENDWARFKSYMS_H */
+336
scripts/gendwarfksyms/kabi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2024 Google LLC 4 + */ 5 + 6 + #define _GNU_SOURCE 7 + #include <errno.h> 8 + #include <stdio.h> 9 + 10 + #include "gendwarfksyms.h" 11 + 12 + #define KABI_RULE_SECTION ".discard.gendwarfksyms.kabi_rules" 13 + #define KABI_RULE_VERSION "1" 14 + 15 + /* 16 + * The rule section consists of four null-terminated strings per 17 + * entry: 18 + * 19 + * 1. version 20 + * Entry format version. Must match KABI_RULE_VERSION. 21 + * 22 + * 2. type 23 + * Type of the kABI rule. Must be one of the tags defined below. 24 + * 25 + * 3. target 26 + * Rule-dependent target, typically the fully qualified name of 27 + * the target DIE. 28 + * 29 + * 4. value 30 + * Rule-dependent value. 31 + */ 32 + #define KABI_RULE_MIN_ENTRY_SIZE \ 33 + (/* version\0 */ 2 + /* type\0 */ 2 + /* target\0" */ 1 + \ 34 + /* value\0 */ 1) 35 + #define KABI_RULE_EMPTY_VALUE "" 36 + 37 + /* 38 + * Rule: declonly 39 + * - For the struct/enum/union in the target field, treat it as a 40 + * declaration only even if a definition is available. 41 + */ 42 + #define KABI_RULE_TAG_DECLONLY "declonly" 43 + 44 + /* 45 + * Rule: enumerator_ignore 46 + * - For the enum_field in the target field, ignore the enumerator. 47 + */ 48 + #define KABI_RULE_TAG_ENUMERATOR_IGNORE "enumerator_ignore" 49 + 50 + /* 51 + * Rule: enumerator_value 52 + * - For the fqn_field in the target field, set the value to the 53 + * unsigned integer in the value field. 54 + */ 55 + #define KABI_RULE_TAG_ENUMERATOR_VALUE "enumerator_value" 56 + 57 + enum kabi_rule_type { 58 + KABI_RULE_TYPE_UNKNOWN, 59 + KABI_RULE_TYPE_DECLONLY, 60 + KABI_RULE_TYPE_ENUMERATOR_IGNORE, 61 + KABI_RULE_TYPE_ENUMERATOR_VALUE, 62 + }; 63 + 64 + #define RULE_HASH_BITS 7 65 + 66 + struct rule { 67 + enum kabi_rule_type type; 68 + const char *target; 69 + const char *value; 70 + struct hlist_node hash; 71 + }; 72 + 73 + /* { type, target } -> struct rule */ 74 + static HASHTABLE_DEFINE(rules, 1 << RULE_HASH_BITS); 75 + 76 + static inline unsigned int rule_values_hash(enum kabi_rule_type type, 77 + const char *target) 78 + { 79 + return hash_32(type) ^ hash_str(target); 80 + } 81 + 82 + static inline unsigned int rule_hash(const struct rule *rule) 83 + { 84 + return rule_values_hash(rule->type, rule->target); 85 + } 86 + 87 + static inline const char *get_rule_field(const char **pos, ssize_t *left) 88 + { 89 + const char *start = *pos; 90 + size_t len; 91 + 92 + if (*left <= 0) 93 + error("unexpected end of kABI rules"); 94 + 95 + len = strnlen(start, *left) + 1; 96 + *pos += len; 97 + *left -= len; 98 + 99 + return start; 100 + } 101 + 102 + void kabi_read_rules(int fd) 103 + { 104 + GElf_Shdr shdr_mem; 105 + GElf_Shdr *shdr; 106 + Elf_Data *rule_data = NULL; 107 + Elf_Scn *scn; 108 + Elf *elf; 109 + size_t shstrndx; 110 + const char *rule_str; 111 + ssize_t left; 112 + int i; 113 + 114 + const struct { 115 + enum kabi_rule_type type; 116 + const char *tag; 117 + } rule_types[] = { 118 + { 119 + .type = KABI_RULE_TYPE_DECLONLY, 120 + .tag = KABI_RULE_TAG_DECLONLY, 121 + }, 122 + { 123 + .type = KABI_RULE_TYPE_ENUMERATOR_IGNORE, 124 + .tag = KABI_RULE_TAG_ENUMERATOR_IGNORE, 125 + }, 126 + { 127 + .type = KABI_RULE_TYPE_ENUMERATOR_VALUE, 128 + .tag = KABI_RULE_TAG_ENUMERATOR_VALUE, 129 + }, 130 + }; 131 + 132 + if (!stable) 133 + return; 134 + 135 + if (elf_version(EV_CURRENT) != EV_CURRENT) 136 + error("elf_version failed: %s", elf_errmsg(-1)); 137 + 138 + elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); 139 + if (!elf) 140 + error("elf_begin failed: %s", elf_errmsg(-1)); 141 + 142 + if (elf_getshdrstrndx(elf, &shstrndx) < 0) 143 + error("elf_getshdrstrndx failed: %s", elf_errmsg(-1)); 144 + 145 + scn = elf_nextscn(elf, NULL); 146 + 147 + while (scn) { 148 + const char *sname; 149 + 150 + shdr = gelf_getshdr(scn, &shdr_mem); 151 + if (!shdr) 152 + error("gelf_getshdr failed: %s", elf_errmsg(-1)); 153 + 154 + sname = elf_strptr(elf, shstrndx, shdr->sh_name); 155 + if (!sname) 156 + error("elf_strptr failed: %s", elf_errmsg(-1)); 157 + 158 + if (!strcmp(sname, KABI_RULE_SECTION)) { 159 + rule_data = elf_getdata(scn, NULL); 160 + if (!rule_data) 161 + error("elf_getdata failed: %s", elf_errmsg(-1)); 162 + break; 163 + } 164 + 165 + scn = elf_nextscn(elf, scn); 166 + } 167 + 168 + if (!rule_data) { 169 + debug("kABI rules not found"); 170 + check(elf_end(elf)); 171 + return; 172 + } 173 + 174 + rule_str = rule_data->d_buf; 175 + left = shdr->sh_size; 176 + 177 + if (left < KABI_RULE_MIN_ENTRY_SIZE) 178 + error("kABI rule section too small: %zd bytes", left); 179 + 180 + if (rule_str[left - 1] != '\0') 181 + error("kABI rules are not null-terminated"); 182 + 183 + while (left > KABI_RULE_MIN_ENTRY_SIZE) { 184 + enum kabi_rule_type type = KABI_RULE_TYPE_UNKNOWN; 185 + const char *field; 186 + struct rule *rule; 187 + 188 + /* version */ 189 + field = get_rule_field(&rule_str, &left); 190 + 191 + if (strcmp(field, KABI_RULE_VERSION)) 192 + error("unsupported kABI rule version: '%s'", field); 193 + 194 + /* type */ 195 + field = get_rule_field(&rule_str, &left); 196 + 197 + for (i = 0; i < ARRAY_SIZE(rule_types); i++) { 198 + if (!strcmp(field, rule_types[i].tag)) { 199 + type = rule_types[i].type; 200 + break; 201 + } 202 + } 203 + 204 + if (type == KABI_RULE_TYPE_UNKNOWN) 205 + error("unsupported kABI rule type: '%s'", field); 206 + 207 + rule = xmalloc(sizeof(struct rule)); 208 + 209 + rule->type = type; 210 + rule->target = xstrdup(get_rule_field(&rule_str, &left)); 211 + rule->value = xstrdup(get_rule_field(&rule_str, &left)); 212 + 213 + hash_add(rules, &rule->hash, rule_hash(rule)); 214 + 215 + debug("kABI rule: type: '%s', target: '%s', value: '%s'", field, 216 + rule->target, rule->value); 217 + } 218 + 219 + if (left > 0) 220 + warn("unexpected data at the end of the kABI rules section"); 221 + 222 + check(elf_end(elf)); 223 + } 224 + 225 + bool kabi_is_declonly(const char *fqn) 226 + { 227 + struct rule *rule; 228 + 229 + if (!stable) 230 + return false; 231 + if (!fqn || !*fqn) 232 + return false; 233 + 234 + hash_for_each_possible(rules, rule, hash, 235 + rule_values_hash(KABI_RULE_TYPE_DECLONLY, fqn)) { 236 + if (rule->type == KABI_RULE_TYPE_DECLONLY && 237 + !strcmp(fqn, rule->target)) 238 + return true; 239 + } 240 + 241 + return false; 242 + } 243 + 244 + static char *get_enumerator_target(const char *fqn, const char *field) 245 + { 246 + char *target = NULL; 247 + 248 + if (asprintf(&target, "%s %s", fqn, field) < 0) 249 + error("asprintf failed for '%s %s'", fqn, field); 250 + 251 + return target; 252 + } 253 + 254 + static unsigned long get_ulong_value(const char *value) 255 + { 256 + unsigned long result = 0; 257 + char *endptr = NULL; 258 + 259 + errno = 0; 260 + result = strtoul(value, &endptr, 10); 261 + 262 + if (errno || *endptr) 263 + error("invalid unsigned value '%s'", value); 264 + 265 + return result; 266 + } 267 + 268 + bool kabi_is_enumerator_ignored(const char *fqn, const char *field) 269 + { 270 + bool match = false; 271 + struct rule *rule; 272 + char *target; 273 + 274 + if (!stable) 275 + return false; 276 + if (!fqn || !*fqn || !field || !*field) 277 + return false; 278 + 279 + target = get_enumerator_target(fqn, field); 280 + 281 + hash_for_each_possible( 282 + rules, rule, hash, 283 + rule_values_hash(KABI_RULE_TYPE_ENUMERATOR_IGNORE, target)) { 284 + if (rule->type == KABI_RULE_TYPE_ENUMERATOR_IGNORE && 285 + !strcmp(target, rule->target)) { 286 + match = true; 287 + break; 288 + } 289 + } 290 + 291 + free(target); 292 + return match; 293 + } 294 + 295 + bool kabi_get_enumerator_value(const char *fqn, const char *field, 296 + unsigned long *value) 297 + { 298 + bool match = false; 299 + struct rule *rule; 300 + char *target; 301 + 302 + if (!stable) 303 + return false; 304 + if (!fqn || !*fqn || !field || !*field) 305 + return false; 306 + 307 + target = get_enumerator_target(fqn, field); 308 + 309 + hash_for_each_possible(rules, rule, hash, 310 + rule_values_hash(KABI_RULE_TYPE_ENUMERATOR_VALUE, 311 + target)) { 312 + if (rule->type == KABI_RULE_TYPE_ENUMERATOR_VALUE && 313 + !strcmp(target, rule->target)) { 314 + *value = get_ulong_value(rule->value); 315 + match = true; 316 + break; 317 + } 318 + } 319 + 320 + free(target); 321 + return match; 322 + } 323 + 324 + void kabi_free(void) 325 + { 326 + struct hlist_node *tmp; 327 + struct rule *rule; 328 + 329 + hash_for_each_safe(rules, rule, tmp, hash) { 330 + free((void *)rule->target); 331 + free((void *)rule->value); 332 + free(rule); 333 + } 334 + 335 + hash_init(rules); 336 + }