Skip to content

Commit a936941

Browse files
samitolvanenmasahir0y
authored andcommitted
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>
1 parent 936cf61 commit a936941

5 files changed

Lines changed: 558 additions & 1 deletion

File tree

scripts/gendwarfksyms/dwarf.c

Lines changed: 247 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,33 @@
33
* Copyright (C) 2024 Google LLC
44
*/
55

6+
#include <assert.h>
67
#include <inttypes.h>
78
#include <stdarg.h>
89
#include "gendwarfksyms.h"
910

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+
};
32+
1033
static bool do_linebreak;
1134
static int indentation_level;
1235

@@ -353,13 +376,23 @@ static void __process_list_type(struct state *state, struct die *cache,
353376
{
354377
const char *name = get_name_attr(die);
355378

379+
if (stable) {
380+
if (is_kabi_prefix(name))
381+
name = NULL;
382+
state->kabi.orig_name = NULL;
383+
}
384+
356385
process_list_comma(state, cache);
357386
process(cache, type);
358387
process_type_attr(state, cache, die);
388+
389+
if (stable && state->kabi.orig_name)
390+
name = state->kabi.orig_name;
359391
if (name) {
360392
process(cache, " ");
361393
process(cache, name);
362394
}
395+
363396
process_accessibility_attr(cache, die);
364397
process_bit_size_attr(cache, die);
365398
process_data_bit_offset_attr(cache, die);
@@ -486,11 +519,208 @@ static void process_variant_part_type(struct state *state, struct die *cache,
486519
process(cache, "}");
487520
}
488521

522+
static int get_kabi_status(Dwarf_Die *die, const char **suffix)
523+
{
524+
const char *name = get_name_attr(die);
525+
526+
if (suffix)
527+
*suffix = NULL;
528+
529+
if (is_kabi_prefix(name)) {
530+
name += KABI_PREFIX_LEN;
531+
532+
if (!strncmp(name, KABI_RESERVED_PREFIX,
533+
KABI_RESERVED_PREFIX_LEN))
534+
return KABI_RESERVED;
535+
if (!strncmp(name, KABI_IGNORED_PREFIX,
536+
KABI_IGNORED_PREFIX_LEN))
537+
return KABI_IGNORED;
538+
539+
if (!strncmp(name, KABI_RENAMED_PREFIX,
540+
KABI_RENAMED_PREFIX_LEN)) {
541+
if (suffix) {
542+
name += KABI_RENAMED_PREFIX_LEN;
543+
*suffix = name;
544+
}
545+
return KABI_RESERVED;
546+
}
547+
}
548+
549+
return KABI_NORMAL;
550+
}
551+
552+
static int check_struct_member_kabi_status(struct state *state,
553+
struct die *__unused, Dwarf_Die *die)
554+
{
555+
int res;
556+
557+
assert(dwarf_tag(die) == DW_TAG_member_type);
558+
559+
/*
560+
* If the union member is a struct, expect the __kabi field to
561+
* be the first member of the structure, i.e..:
562+
*
563+
* union {
564+
* type new_member;
565+
* struct {
566+
* type __kabi_field;
567+
* }
568+
* };
569+
*/
570+
res = get_kabi_status(die, &state->kabi.orig_name);
571+
572+
if (res == KABI_RESERVED &&
573+
!get_ref_die_attr(die, DW_AT_type, &state->kabi.placeholder))
574+
error("structure member missing a type?");
575+
576+
return res;
577+
}
578+
579+
static int check_union_member_kabi_status(struct state *state,
580+
struct die *__unused, Dwarf_Die *die)
581+
{
582+
Dwarf_Die type;
583+
int res;
584+
585+
assert(dwarf_tag(die) == DW_TAG_member_type);
586+
587+
if (!get_ref_die_attr(die, DW_AT_type, &type))
588+
error("union member missing a type?");
589+
590+
/*
591+
* We expect a union with two members. Check if either of them
592+
* has a __kabi name prefix, i.e.:
593+
*
594+
* union {
595+
* ...
596+
* type memberN; // <- type, N = {0,1}
597+
* ...
598+
* };
599+
*
600+
* The member can also be a structure type, in which case we'll
601+
* check the first structure member.
602+
*
603+
* In any case, stop processing after we've seen two members.
604+
*/
605+
res = get_kabi_status(die, &state->kabi.orig_name);
606+
607+
if (res == KABI_RESERVED)
608+
state->kabi.placeholder = type;
609+
if (res != KABI_NORMAL)
610+
return res;
611+
612+
if (dwarf_tag(&type) == DW_TAG_structure_type)
613+
res = checkp(process_die_container(
614+
state, NULL, &type, check_struct_member_kabi_status,
615+
match_member_type));
616+
617+
if (res <= KABI_NORMAL && ++state->kabi.members < 2)
618+
return 0; /* Continue */
619+
620+
return res;
621+
}
622+
623+
static int get_union_kabi_status(Dwarf_Die *die, Dwarf_Die *placeholder,
624+
const char **orig_name)
625+
{
626+
struct state state;
627+
int res;
628+
629+
if (!stable)
630+
return KABI_NORMAL;
631+
632+
/*
633+
* To maintain a stable kABI, distributions may choose to reserve
634+
* space in structs for later use by adding placeholder members,
635+
* for example:
636+
*
637+
* struct s {
638+
* u32 a;
639+
* // an 8-byte placeholder for future use
640+
* u64 __kabi_reserved_0;
641+
* };
642+
*
643+
* When the reserved member is taken into use, the type change
644+
* would normally cause the symbol version to change as well, but
645+
* if the replacement uses the following convention, gendwarfksyms
646+
* continues to use the placeholder type for versioning instead,
647+
* thus maintaining the same symbol version:
648+
*
649+
* struct s {
650+
* u32 a;
651+
* union {
652+
* // placeholder replaced with a new member `b`
653+
* struct t b;
654+
* struct {
655+
* // the placeholder type that is still
656+
* // used for versioning
657+
* u64 __kabi_reserved_0;
658+
* };
659+
* };
660+
* };
661+
*
662+
* I.e., as long as the replaced member is in a union, and the
663+
* placeholder has a __kabi_reserved name prefix, we'll continue
664+
* to use the placeholder type (here u64) for version calculation
665+
* instead of the union type.
666+
*
667+
* It's also possible to ignore new members from versioning if
668+
* they've been added to alignment holes, for example, by
669+
* including them in a union with another member that uses the
670+
* __kabi_ignored name prefix:
671+
*
672+
* struct s {
673+
* u32 a;
674+
* // an alignment hole is used to add `n`
675+
* union {
676+
* u32 n;
677+
* // hide the entire union member from versioning
678+
* u8 __kabi_ignored_0;
679+
* };
680+
* u64 b;
681+
* };
682+
*
683+
* Note that the user of this feature is responsible for ensuring
684+
* that the structure actually remains ABI compatible.
685+
*/
686+
memset(&state.kabi, 0, sizeof(struct kabi_state));
687+
688+
res = checkp(process_die_container(&state, NULL, die,
689+
check_union_member_kabi_status,
690+
match_member_type));
691+
692+
if (res == KABI_RESERVED) {
693+
if (placeholder)
694+
*placeholder = state.kabi.placeholder;
695+
if (orig_name)
696+
*orig_name = state.kabi.orig_name;
697+
}
698+
699+
return res;
700+
}
701+
702+
static bool is_kabi_ignored(Dwarf_Die *die)
703+
{
704+
Dwarf_Die type;
705+
706+
if (!stable)
707+
return false;
708+
709+
if (!get_ref_die_attr(die, DW_AT_type, &type))
710+
error("member missing a type?");
711+
712+
return dwarf_tag(&type) == DW_TAG_union_type &&
713+
checkp(get_union_kabi_status(&type, NULL, NULL)) == KABI_IGNORED;
714+
}
715+
489716
static int ___process_structure_type(struct state *state, struct die *cache,
490717
Dwarf_Die *die)
491718
{
492719
switch (dwarf_tag(die)) {
493720
case DW_TAG_member:
721+
if (is_kabi_ignored(die))
722+
return 0;
723+
return check(process_type(state, cache, die));
494724
case DW_TAG_variant_part:
495725
return check(process_type(state, cache, die));
496726
case DW_TAG_class_type:
@@ -547,7 +777,23 @@ static void __process_structure_type(struct state *state, struct die *cache,
547777

548778
DEFINE_PROCESS_STRUCTURE_TYPE(class)
549779
DEFINE_PROCESS_STRUCTURE_TYPE(structure)
550-
DEFINE_PROCESS_STRUCTURE_TYPE(union)
780+
781+
static void process_union_type(struct state *state, struct die *cache,
782+
Dwarf_Die *die)
783+
{
784+
Dwarf_Die placeholder;
785+
786+
int res = checkp(get_union_kabi_status(die, &placeholder,
787+
&state->kabi.orig_name));
788+
789+
if (res == KABI_RESERVED)
790+
check(process_type(state, cache, &placeholder));
791+
if (res > KABI_NORMAL)
792+
return;
793+
794+
__process_structure_type(state, cache, die, "union_type",
795+
___process_structure_type, match_all);
796+
}
551797

552798
static void process_enumerator_type(struct state *state, struct die *cache,
553799
Dwarf_Die *die)

scripts/gendwarfksyms/examples/kabi.h

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,28 @@
4343
__section(".discard.gendwarfksyms.kabi_rules") = \
4444
"1\0" #hint "\0" #target "\0" #value
4545

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+
4668
/*
4769
* KABI_DECLONLY(fqn)
4870
* Treat the struct/union/enum fqn as a declaration, i.e. even if
@@ -67,4 +89,69 @@
6789
#define KABI_ENUMERATOR_VALUE(fqn, field, value) \
6890
__KABI_RULE(enumerator_value, fqn field, value)
6991

92+
/*
93+
* KABI_RESERVE
94+
* Reserve some "padding" in a structure for use by LTS backports.
95+
* This is normally placed at the end of a structure.
96+
* number: the "number" of the padding variable in the structure. Start with
97+
* 1 and go up.
98+
*/
99+
#define KABI_RESERVE(n) unsigned long __kabi_reserved##n
100+
101+
/*
102+
* KABI_RESERVE_ARRAY
103+
* Same as _BACKPORT_RESERVE but allocates an array with the specified
104+
* size in bytes.
105+
*/
106+
#define KABI_RESERVE_ARRAY(n, s) \
107+
unsigned char __aligned(8) __kabi_reserved##n[s]
108+
109+
/*
110+
* KABI_IGNORE
111+
* Add a new field that's ignored in versioning.
112+
*/
113+
#define KABI_IGNORE(n, _new) \
114+
union { \
115+
_new; \
116+
unsigned char __kabi_ignored##n; \
117+
}
118+
119+
/*
120+
* KABI_REPLACE
121+
* Replace a field with a compatible new field.
122+
*/
123+
#define KABI_REPLACE(_oldtype, _oldname, _new) \
124+
__KABI_REPLACE(_oldtype __kabi_renamed##_oldname, struct { _new; })
125+
126+
/*
127+
* KABI_USE(number, _new)
128+
* Use a previous padding entry that was defined with KABI_RESERVE
129+
* number: the previous "number" of the padding variable
130+
* _new: the variable to use now instead of the padding variable
131+
*/
132+
#define KABI_USE(number, _new) __KABI_REPLACE(KABI_RESERVE(number), _new)
133+
134+
/*
135+
* KABI_USE2(number, _new1, _new2)
136+
* Use a previous padding entry that was defined with KABI_RESERVE for
137+
* two new variables that fit into 64 bits. This is good for when you do not
138+
* want to "burn" a 64bit padding variable for a smaller variable size if not
139+
* needed.
140+
*/
141+
#define KABI_USE2(number, _new1, _new2) \
142+
__KABI_REPLACE( \
143+
KABI_RESERVE(number), struct { \
144+
_new1; \
145+
_new2; \
146+
})
147+
/*
148+
* KABI_USE_ARRAY(number, bytes, _new)
149+
* Use a previous padding entry that was defined with KABI_RESERVE_ARRAY
150+
* number: the previous "number" of the padding variable
151+
* bytes: the size in bytes reserved for the array
152+
* _new: the variable to use now instead of the padding variable
153+
*/
154+
#define KABI_USE_ARRAY(number, bytes, _new) \
155+
__KABI_REPLACE(KABI_RESERVE_ARRAY(number, bytes), _new)
156+
70157
#endif /* __KABI_H__ */

0 commit comments

Comments
 (0)