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

Configure Feed

Select the types of activity you want to include in your feed.

gendwarfksyms: Add support for reserved and ignored fields

Distributions that want to maintain a stable kABI need the ability
to make ABI compatible changes to kernel data structures 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.

Change union processing to recognize field name prefixes that allow
the user to ignore the union completely during symbol versioning with
a __kabi_ignored prefix in a field name, or to replace the type of a
placeholder field using a __kabi_reserved field name prefix.

For example, assume we want to add a new field to an existing
alignment hole in a data structure, and ignore the new field when
calculating symbol versions:

struct struct1 {
int a;
/* a 4-byte alignment hole */
unsigned long b;
};

To add `int n` to the alignment hole, we can add a union that includes
a __kabi_ignored field that causes gendwarfksyms to ignore the entire
union:

struct struct1 {
int a;
union {
char __kabi_ignored_0;
int n;
};
unsigned long b;
};

With --stable, both structs produce the same symbol version.

Alternatively, when a distribution expects future modification to a
data structure, they can explicitly add reserved fields:

struct struct2 {
long a;
long __kabi_reserved_0; /* reserved for future use */
};

To take the field into use, we can again replace it with a union, with
one of the fields keeping the __kabi_reserved name prefix to indicate
the original type:

struct struct2 {
long a;
union {
long __kabi_reserved_0;
struct {
int b;
int v;
};
};

Here gendwarfksyms --stable replaces the union with the type of the
placeholder field when calculating versions.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Petr Pavlu <petr.pavlu@suse.com>
Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>

authored by

Sami Tolvanen and committed by
Masahiro Yamada
a9369418 936cf61c

+558 -1
+247 -1
scripts/gendwarfksyms/dwarf.c
··· 3 3 * Copyright (C) 2024 Google LLC 4 4 */ 5 5 6 + #include <assert.h> 6 7 #include <inttypes.h> 7 8 #include <stdarg.h> 8 9 #include "gendwarfksyms.h" 10 + 11 + /* See get_union_kabi_status */ 12 + #define KABI_PREFIX "__kabi_" 13 + #define KABI_PREFIX_LEN (sizeof(KABI_PREFIX) - 1) 14 + #define KABI_RESERVED_PREFIX "reserved" 15 + #define KABI_RESERVED_PREFIX_LEN (sizeof(KABI_RESERVED_PREFIX) - 1) 16 + #define KABI_RENAMED_PREFIX "renamed" 17 + #define KABI_RENAMED_PREFIX_LEN (sizeof(KABI_RENAMED_PREFIX) - 1) 18 + #define KABI_IGNORED_PREFIX "ignored" 19 + #define KABI_IGNORED_PREFIX_LEN (sizeof(KABI_IGNORED_PREFIX) - 1) 20 + 21 + static inline bool is_kabi_prefix(const char *name) 22 + { 23 + return name && !strncmp(name, KABI_PREFIX, KABI_PREFIX_LEN); 24 + } 25 + 26 + enum kabi_status { 27 + /* >0 to stop DIE processing */ 28 + KABI_NORMAL = 1, 29 + KABI_RESERVED, 30 + KABI_IGNORED, 31 + }; 9 32 10 33 static bool do_linebreak; 11 34 static int indentation_level; ··· 376 353 { 377 354 const char *name = get_name_attr(die); 378 355 356 + if (stable) { 357 + if (is_kabi_prefix(name)) 358 + name = NULL; 359 + state->kabi.orig_name = NULL; 360 + } 361 + 379 362 process_list_comma(state, cache); 380 363 process(cache, type); 381 364 process_type_attr(state, cache, die); 365 + 366 + if (stable && state->kabi.orig_name) 367 + name = state->kabi.orig_name; 382 368 if (name) { 383 369 process(cache, " "); 384 370 process(cache, name); 385 371 } 372 + 386 373 process_accessibility_attr(cache, die); 387 374 process_bit_size_attr(cache, die); 388 375 process_data_bit_offset_attr(cache, die); ··· 519 486 process(cache, "}"); 520 487 } 521 488 489 + static int get_kabi_status(Dwarf_Die *die, const char **suffix) 490 + { 491 + const char *name = get_name_attr(die); 492 + 493 + if (suffix) 494 + *suffix = NULL; 495 + 496 + if (is_kabi_prefix(name)) { 497 + name += KABI_PREFIX_LEN; 498 + 499 + if (!strncmp(name, KABI_RESERVED_PREFIX, 500 + KABI_RESERVED_PREFIX_LEN)) 501 + return KABI_RESERVED; 502 + if (!strncmp(name, KABI_IGNORED_PREFIX, 503 + KABI_IGNORED_PREFIX_LEN)) 504 + return KABI_IGNORED; 505 + 506 + if (!strncmp(name, KABI_RENAMED_PREFIX, 507 + KABI_RENAMED_PREFIX_LEN)) { 508 + if (suffix) { 509 + name += KABI_RENAMED_PREFIX_LEN; 510 + *suffix = name; 511 + } 512 + return KABI_RESERVED; 513 + } 514 + } 515 + 516 + return KABI_NORMAL; 517 + } 518 + 519 + static int check_struct_member_kabi_status(struct state *state, 520 + struct die *__unused, Dwarf_Die *die) 521 + { 522 + int res; 523 + 524 + assert(dwarf_tag(die) == DW_TAG_member_type); 525 + 526 + /* 527 + * If the union member is a struct, expect the __kabi field to 528 + * be the first member of the structure, i.e..: 529 + * 530 + * union { 531 + * type new_member; 532 + * struct { 533 + * type __kabi_field; 534 + * } 535 + * }; 536 + */ 537 + res = get_kabi_status(die, &state->kabi.orig_name); 538 + 539 + if (res == KABI_RESERVED && 540 + !get_ref_die_attr(die, DW_AT_type, &state->kabi.placeholder)) 541 + error("structure member missing a type?"); 542 + 543 + return res; 544 + } 545 + 546 + static int check_union_member_kabi_status(struct state *state, 547 + struct die *__unused, Dwarf_Die *die) 548 + { 549 + Dwarf_Die type; 550 + int res; 551 + 552 + assert(dwarf_tag(die) == DW_TAG_member_type); 553 + 554 + if (!get_ref_die_attr(die, DW_AT_type, &type)) 555 + error("union member missing a type?"); 556 + 557 + /* 558 + * We expect a union with two members. Check if either of them 559 + * has a __kabi name prefix, i.e.: 560 + * 561 + * union { 562 + * ... 563 + * type memberN; // <- type, N = {0,1} 564 + * ... 565 + * }; 566 + * 567 + * The member can also be a structure type, in which case we'll 568 + * check the first structure member. 569 + * 570 + * In any case, stop processing after we've seen two members. 571 + */ 572 + res = get_kabi_status(die, &state->kabi.orig_name); 573 + 574 + if (res == KABI_RESERVED) 575 + state->kabi.placeholder = type; 576 + if (res != KABI_NORMAL) 577 + return res; 578 + 579 + if (dwarf_tag(&type) == DW_TAG_structure_type) 580 + res = checkp(process_die_container( 581 + state, NULL, &type, check_struct_member_kabi_status, 582 + match_member_type)); 583 + 584 + if (res <= KABI_NORMAL && ++state->kabi.members < 2) 585 + return 0; /* Continue */ 586 + 587 + return res; 588 + } 589 + 590 + static int get_union_kabi_status(Dwarf_Die *die, Dwarf_Die *placeholder, 591 + const char **orig_name) 592 + { 593 + struct state state; 594 + int res; 595 + 596 + if (!stable) 597 + return KABI_NORMAL; 598 + 599 + /* 600 + * To maintain a stable kABI, distributions may choose to reserve 601 + * space in structs for later use by adding placeholder members, 602 + * for example: 603 + * 604 + * struct s { 605 + * u32 a; 606 + * // an 8-byte placeholder for future use 607 + * u64 __kabi_reserved_0; 608 + * }; 609 + * 610 + * When the reserved member is taken into use, the type change 611 + * would normally cause the symbol version to change as well, but 612 + * if the replacement uses the following convention, gendwarfksyms 613 + * continues to use the placeholder type for versioning instead, 614 + * thus maintaining the same symbol version: 615 + * 616 + * struct s { 617 + * u32 a; 618 + * union { 619 + * // placeholder replaced with a new member `b` 620 + * struct t b; 621 + * struct { 622 + * // the placeholder type that is still 623 + * // used for versioning 624 + * u64 __kabi_reserved_0; 625 + * }; 626 + * }; 627 + * }; 628 + * 629 + * I.e., as long as the replaced member is in a union, and the 630 + * placeholder has a __kabi_reserved name prefix, we'll continue 631 + * to use the placeholder type (here u64) for version calculation 632 + * instead of the union type. 633 + * 634 + * It's also possible to ignore new members from versioning if 635 + * they've been added to alignment holes, for example, by 636 + * including them in a union with another member that uses the 637 + * __kabi_ignored name prefix: 638 + * 639 + * struct s { 640 + * u32 a; 641 + * // an alignment hole is used to add `n` 642 + * union { 643 + * u32 n; 644 + * // hide the entire union member from versioning 645 + * u8 __kabi_ignored_0; 646 + * }; 647 + * u64 b; 648 + * }; 649 + * 650 + * Note that the user of this feature is responsible for ensuring 651 + * that the structure actually remains ABI compatible. 652 + */ 653 + memset(&state.kabi, 0, sizeof(struct kabi_state)); 654 + 655 + res = checkp(process_die_container(&state, NULL, die, 656 + check_union_member_kabi_status, 657 + match_member_type)); 658 + 659 + if (res == KABI_RESERVED) { 660 + if (placeholder) 661 + *placeholder = state.kabi.placeholder; 662 + if (orig_name) 663 + *orig_name = state.kabi.orig_name; 664 + } 665 + 666 + return res; 667 + } 668 + 669 + static bool is_kabi_ignored(Dwarf_Die *die) 670 + { 671 + Dwarf_Die type; 672 + 673 + if (!stable) 674 + return false; 675 + 676 + if (!get_ref_die_attr(die, DW_AT_type, &type)) 677 + error("member missing a type?"); 678 + 679 + return dwarf_tag(&type) == DW_TAG_union_type && 680 + checkp(get_union_kabi_status(&type, NULL, NULL)) == KABI_IGNORED; 681 + } 682 + 522 683 static int ___process_structure_type(struct state *state, struct die *cache, 523 684 Dwarf_Die *die) 524 685 { 525 686 switch (dwarf_tag(die)) { 526 687 case DW_TAG_member: 688 + if (is_kabi_ignored(die)) 689 + return 0; 690 + return check(process_type(state, cache, die)); 527 691 case DW_TAG_variant_part: 528 692 return check(process_type(state, cache, die)); 529 693 case DW_TAG_class_type: ··· 777 547 778 548 DEFINE_PROCESS_STRUCTURE_TYPE(class) 779 549 DEFINE_PROCESS_STRUCTURE_TYPE(structure) 780 - DEFINE_PROCESS_STRUCTURE_TYPE(union) 550 + 551 + static void process_union_type(struct state *state, struct die *cache, 552 + Dwarf_Die *die) 553 + { 554 + Dwarf_Die placeholder; 555 + 556 + int res = checkp(get_union_kabi_status(die, &placeholder, 557 + &state->kabi.orig_name)); 558 + 559 + if (res == KABI_RESERVED) 560 + check(process_type(state, cache, &placeholder)); 561 + if (res > KABI_NORMAL) 562 + return; 563 + 564 + __process_structure_type(state, cache, die, "union_type", 565 + ___process_structure_type, match_all); 566 + } 781 567 782 568 static void process_enumerator_type(struct state *state, struct die *cache, 783 569 Dwarf_Die *die)
+87
scripts/gendwarfksyms/examples/kabi.h
··· 43 43 __section(".discard.gendwarfksyms.kabi_rules") = \ 44 44 "1\0" #hint "\0" #target "\0" #value 45 45 46 + #define __KABI_NORMAL_SIZE_ALIGN(_orig, _new) \ 47 + union { \ 48 + _Static_assert( \ 49 + sizeof(struct { _new; }) <= sizeof(struct { _orig; }), \ 50 + __FILE__ ":" __stringify(__LINE__) ": " __stringify( \ 51 + _new) " is larger than " __stringify(_orig)); \ 52 + _Static_assert( \ 53 + __alignof__(struct { _new; }) <= \ 54 + __alignof__(struct { _orig; }), \ 55 + __FILE__ ":" __stringify(__LINE__) ": " __stringify( \ 56 + _orig) " is not aligned the same as " __stringify(_new)); \ 57 + } 58 + 59 + #define __KABI_REPLACE(_orig, _new) \ 60 + union { \ 61 + _new; \ 62 + struct { \ 63 + _orig; \ 64 + }; \ 65 + __KABI_NORMAL_SIZE_ALIGN(_orig, _new); \ 66 + } 67 + 46 68 /* 47 69 * KABI_DECLONLY(fqn) 48 70 * Treat the struct/union/enum fqn as a declaration, i.e. even if ··· 88 66 */ 89 67 #define KABI_ENUMERATOR_VALUE(fqn, field, value) \ 90 68 __KABI_RULE(enumerator_value, fqn field, value) 69 + 70 + /* 71 + * KABI_RESERVE 72 + * Reserve some "padding" in a structure for use by LTS backports. 73 + * This is normally placed at the end of a structure. 74 + * number: the "number" of the padding variable in the structure. Start with 75 + * 1 and go up. 76 + */ 77 + #define KABI_RESERVE(n) unsigned long __kabi_reserved##n 78 + 79 + /* 80 + * KABI_RESERVE_ARRAY 81 + * Same as _BACKPORT_RESERVE but allocates an array with the specified 82 + * size in bytes. 83 + */ 84 + #define KABI_RESERVE_ARRAY(n, s) \ 85 + unsigned char __aligned(8) __kabi_reserved##n[s] 86 + 87 + /* 88 + * KABI_IGNORE 89 + * Add a new field that's ignored in versioning. 90 + */ 91 + #define KABI_IGNORE(n, _new) \ 92 + union { \ 93 + _new; \ 94 + unsigned char __kabi_ignored##n; \ 95 + } 96 + 97 + /* 98 + * KABI_REPLACE 99 + * Replace a field with a compatible new field. 100 + */ 101 + #define KABI_REPLACE(_oldtype, _oldname, _new) \ 102 + __KABI_REPLACE(_oldtype __kabi_renamed##_oldname, struct { _new; }) 103 + 104 + /* 105 + * KABI_USE(number, _new) 106 + * Use a previous padding entry that was defined with KABI_RESERVE 107 + * number: the previous "number" of the padding variable 108 + * _new: the variable to use now instead of the padding variable 109 + */ 110 + #define KABI_USE(number, _new) __KABI_REPLACE(KABI_RESERVE(number), _new) 111 + 112 + /* 113 + * KABI_USE2(number, _new1, _new2) 114 + * Use a previous padding entry that was defined with KABI_RESERVE for 115 + * two new variables that fit into 64 bits. This is good for when you do not 116 + * want to "burn" a 64bit padding variable for a smaller variable size if not 117 + * needed. 118 + */ 119 + #define KABI_USE2(number, _new1, _new2) \ 120 + __KABI_REPLACE( \ 121 + KABI_RESERVE(number), struct { \ 122 + _new1; \ 123 + _new2; \ 124 + }) 125 + /* 126 + * KABI_USE_ARRAY(number, bytes, _new) 127 + * Use a previous padding entry that was defined with KABI_RESERVE_ARRAY 128 + * number: the previous "number" of the padding variable 129 + * bytes: the size in bytes reserved for the array 130 + * _new: the variable to use now instead of the padding variable 131 + */ 132 + #define KABI_USE_ARRAY(number, bytes, _new) \ 133 + __KABI_REPLACE(KABI_RESERVE_ARRAY(number, bytes), _new) 91 134 92 135 #endif /* __KABI_H__ */
+16
scripts/gendwarfksyms/examples/kabi_ex.c
··· 12 12 13 13 struct s e0; 14 14 enum e e1; 15 + 16 + struct ex0a ex0a; 17 + struct ex0b ex0b; 18 + struct ex0c ex0c; 19 + 20 + struct ex1a ex1a; 21 + struct ex1b ex1b; 22 + struct ex1c ex1c; 23 + 24 + struct ex2a ex2a; 25 + struct ex2b ex2b; 26 + struct ex2c ex2c; 27 + 28 + struct ex3a ex3a; 29 + struct ex3b ex3b; 30 + struct ex3c ex3c;
+199
scripts/gendwarfksyms/examples/kabi_ex.h
··· 59 59 * STABLE-NEXT: enumerator A = 0 , 60 60 * STABLE-NEXT: enumerator D = 123456789 61 61 * STABLE-NEXT: } byte_size(4) 62 + */ 63 + 64 + /* 65 + * Example: Reserved fields 66 + */ 67 + struct ex0a { 68 + int a; 69 + KABI_RESERVE(0); 70 + KABI_RESERVE(1); 71 + }; 72 + 73 + /* 74 + * STABLE: variable structure_type ex0a { 75 + * STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) , 76 + * STABLE-NEXT: member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) data_member_location(8) , 77 + * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(16) 78 + * STABLE-NEXT: } byte_size(24) 79 + */ 80 + 81 + struct ex0b { 82 + int a; 83 + KABI_RESERVE(0); 84 + KABI_USE2(1, int b, int c); 85 + }; 86 + 87 + /* 88 + * STABLE: variable structure_type ex0b { 89 + * STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) , 90 + * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(8) , 91 + * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(16) 92 + * STABLE-NEXT: } byte_size(24) 93 + */ 94 + 95 + struct ex0c { 96 + int a; 97 + KABI_USE(0, void *p); 98 + KABI_USE2(1, int b, int c); 99 + }; 100 + 101 + /* 102 + * STABLE: variable structure_type ex0c { 103 + * STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) , 104 + * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(8) , 105 + * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(16) 106 + * STABLE-NEXT: } byte_size(24) 107 + */ 108 + 109 + /* 110 + * Example: A reserved array 111 + */ 112 + 113 + struct ex1a { 114 + unsigned int a; 115 + KABI_RESERVE_ARRAY(0, 64); 116 + }; 117 + 118 + /* 119 + * STABLE: variable structure_type ex1a { 120 + * STABLE-NEXT: member base_type unsigned int byte_size(4) encoding(7) a data_member_location(0) , 121 + * STABLE-NEXT: member array_type[64] { 122 + * STABLE-NEXT: base_type unsigned char byte_size(1) encoding(8) 123 + * STABLE-NEXT: } data_member_location(8) 124 + * STABLE-NEXT: } byte_size(72) 125 + */ 126 + 127 + struct ex1b { 128 + unsigned int a; 129 + KABI_USE_ARRAY( 130 + 0, 64, struct { 131 + void *p; 132 + KABI_RESERVE_ARRAY(1, 56); 133 + }); 134 + }; 135 + 136 + /* 137 + * STABLE: variable structure_type ex1b { 138 + * STABLE-NEXT: member base_type unsigned int byte_size(4) encoding(7) a data_member_location(0) , 139 + * STABLE-NEXT: member array_type[64] { 140 + * STABLE-NEXT: base_type unsigned char byte_size(1) encoding(8) 141 + * STABLE-NEXT: } data_member_location(8) 142 + * STABLE-NEXT: } byte_size(72) 143 + */ 144 + 145 + struct ex1c { 146 + unsigned int a; 147 + KABI_USE_ARRAY(0, 64, void *p[8]); 148 + }; 149 + 150 + /* 151 + * STABLE: variable structure_type ex1c { 152 + * STABLE-NEXT: member base_type unsigned int byte_size(4) encoding(7) a data_member_location(0) , 153 + * STABLE-NEXT: member array_type[64] { 154 + * STABLE-NEXT: base_type unsigned char byte_size(1) encoding(8) 155 + * STABLE-NEXT: } data_member_location(8) 156 + * STABLE-NEXT: } byte_size(72) 157 + */ 158 + 159 + /* 160 + * Example: An ignored field added to an alignment hole 161 + */ 162 + 163 + struct ex2a { 164 + int a; 165 + unsigned long b; 166 + int c; 167 + unsigned long d; 168 + }; 169 + 170 + /* 171 + * STABLE: variable structure_type ex2a { 172 + * STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) , 173 + * STABLE-NEXT: member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) b data_member_location(8) 174 + * STABLE-NEXT: member base_type int byte_size(4) encoding(5) c data_member_location(16) , 175 + * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) d data_member_location(24) 176 + * STABLE-NEXT: } byte_size(32) 177 + */ 178 + 179 + struct ex2b { 180 + int a; 181 + KABI_IGNORE(0, unsigned int n); 182 + unsigned long b; 183 + int c; 184 + unsigned long d; 185 + }; 186 + 187 + _Static_assert(sizeof(struct ex2a) == sizeof(struct ex2b), "ex2a size doesn't match ex2b"); 188 + 189 + /* 190 + * STABLE: variable structure_type ex2b { 191 + * STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) , 192 + * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) b data_member_location(8) 193 + * STABLE-NEXT: member base_type int byte_size(4) encoding(5) c data_member_location(16) , 194 + * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) d data_member_location(24) 195 + * STABLE-NEXT: } byte_size(32) 196 + */ 197 + 198 + struct ex2c { 199 + int a; 200 + KABI_IGNORE(0, unsigned int n); 201 + unsigned long b; 202 + int c; 203 + KABI_IGNORE(1, unsigned int m); 204 + unsigned long d; 205 + }; 206 + 207 + _Static_assert(sizeof(struct ex2a) == sizeof(struct ex2c), "ex2a size doesn't match ex2c"); 208 + 209 + /* 210 + * STABLE: variable structure_type ex2c { 211 + * STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) , 212 + * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) b data_member_location(8) 213 + * STABLE-NEXT: member base_type int byte_size(4) encoding(5) c data_member_location(16) , 214 + * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) d data_member_location(24) 215 + * STABLE-NEXT: } byte_size(32) 216 + */ 217 + 218 + 219 + /* 220 + * Example: A replaced field 221 + */ 222 + 223 + struct ex3a { 224 + unsigned long a; 225 + unsigned long unused; 226 + }; 227 + 228 + /* 229 + * STABLE: variable structure_type ex3a { 230 + * STABLE-NEXT: member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) a data_member_location(0) 231 + * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) unused data_member_location(8) 232 + * STABLE-NEXT: } byte_size(16) 233 + */ 234 + 235 + struct ex3b { 236 + unsigned long a; 237 + KABI_REPLACE(unsigned long, unused, unsigned long renamed); 238 + }; 239 + 240 + _Static_assert(sizeof(struct ex3a) == sizeof(struct ex3b), "ex3a size doesn't match ex3b"); 241 + 242 + /* 243 + * STABLE: variable structure_type ex3b { 244 + * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) a data_member_location(0) 245 + * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) unused data_member_location(8) 246 + * STABLE-NEXT: } byte_size(16) 247 + */ 248 + 249 + struct ex3c { 250 + unsigned long a; 251 + KABI_REPLACE(unsigned long, unused, long replaced); 252 + }; 253 + 254 + _Static_assert(sizeof(struct ex3a) == sizeof(struct ex3c), "ex3a size doesn't match ex3c"); 255 + 256 + /* 257 + * STABLE: variable structure_type ex3c { 258 + * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) a data_member_location(0) 259 + * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) unused data_member_location(8) 260 + * STABLE-NEXT: } byte_size(16) 62 261 */ 63 262 64 263 #endif /* __KABI_EX_H__ */
+9
scripts/gendwarfksyms/gendwarfksyms.h
··· 236 236 const char *current_fqn; 237 237 }; 238 238 239 + struct kabi_state { 240 + int members; 241 + Dwarf_Die placeholder; 242 + const char *orig_name; 243 + }; 244 + 239 245 struct state { 240 246 struct symbol *sym; 241 247 Dwarf_Die die; ··· 252 246 /* Structure expansion */ 253 247 struct expansion_state expand; 254 248 struct cache expansion_cache; 249 + 250 + /* Reserved or ignored members */ 251 + struct kabi_state kabi; 255 252 }; 256 253 257 254 typedef int (*die_callback_t)(struct state *state, struct die *cache,