Skip to content

Commit 936cf61

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

8 files changed

Lines changed: 531 additions & 4 deletions

File tree

scripts/gendwarfksyms/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ gendwarfksyms-objs += gendwarfksyms.o
55
gendwarfksyms-objs += cache.o
66
gendwarfksyms-objs += die.o
77
gendwarfksyms-objs += dwarf.o
8+
gendwarfksyms-objs += kabi.o
89
gendwarfksyms-objs += symbols.o
910
gendwarfksyms-objs += types.o
1011

scripts/gendwarfksyms/dwarf.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,16 @@ static bool is_definition_private(Dwarf_Die *die)
120120
return !!res;
121121
}
122122

123-
static bool is_kabi_definition(Dwarf_Die *die)
123+
static bool is_kabi_definition(struct die *cache, Dwarf_Die *die)
124124
{
125125
bool value;
126126

127127
if (get_flag_attr(die, DW_AT_declaration, &value) && value)
128128
return false;
129129

130+
if (kabi_is_declonly(cache->fqn))
131+
return false;
132+
130133
return !is_definition_private(die);
131134
}
132135

@@ -515,9 +518,10 @@ static void __process_structure_type(struct state *state, struct die *cache,
515518
process(cache, " {");
516519
process_linebreak(cache, 1);
517520

518-
expand = state->expand.expand && is_kabi_definition(die);
521+
expand = state->expand.expand && is_kabi_definition(cache, die);
519522

520523
if (expand) {
524+
state->expand.current_fqn = cache->fqn;
521525
check(process_die_container(state, cache, die, process_func,
522526
match_func));
523527
}
@@ -548,13 +552,26 @@ DEFINE_PROCESS_STRUCTURE_TYPE(union)
548552
static void process_enumerator_type(struct state *state, struct die *cache,
549553
Dwarf_Die *die)
550554
{
555+
bool overridden = false;
551556
Dwarf_Word value;
552557

558+
if (stable) {
559+
/* Get the fqn before we process anything */
560+
update_fqn(cache, die);
561+
562+
if (kabi_is_enumerator_ignored(state->expand.current_fqn,
563+
cache->fqn))
564+
return;
565+
566+
overridden = kabi_get_enumerator_value(
567+
state->expand.current_fqn, cache->fqn, &value);
568+
}
569+
553570
process_list_comma(state, cache);
554571
process(cache, "enumerator");
555572
process_fqn(cache, die);
556573

557-
if (get_udata_attr(die, DW_AT_const_value, &value)) {
574+
if (overridden || get_udata_attr(die, DW_AT_const_value, &value)) {
558575
process(cache, " = ");
559576
process_fmt(cache, "%" PRIu64, value);
560577
}
@@ -620,13 +637,15 @@ static void process_cached(struct state *state, struct die *cache,
620637
static void state_init(struct state *state)
621638
{
622639
state->expand.expand = true;
640+
state->expand.current_fqn = NULL;
623641
cache_init(&state->expansion_cache);
624642
}
625643

626644
static void expansion_state_restore(struct expansion_state *state,
627645
struct expansion_state *saved)
628646
{
629647
state->expand = saved->expand;
648+
state->current_fqn = saved->current_fqn;
630649
}
631650

632651
static void expansion_state_save(struct expansion_state *state,
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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__ */
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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;
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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__ */

scripts/gendwarfksyms/gendwarfksyms.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ int dump_die_map;
2525
int dump_types;
2626
/* Print out expanded type strings used for symbol versions */
2727
int dump_versions;
28+
/* Support kABI stability features */
29+
int stable;
2830
/* Write a symtypes file */
2931
int symtypes;
3032
static const char *symtypes_file;
@@ -38,6 +40,7 @@ static void usage(void)
3840
" --dump-die-map Print debugging information about die_map changes\n"
3941
" --dump-types Dump type strings\n"
4042
" --dump-versions Dump expanded type strings used for symbol versions\n"
43+
" -s, --stable Support kABI stability features\n"
4144
" -T, --symtypes file Write a symtypes file\n"
4245
" -h, --help Print this message\n"
4346
"\n",
@@ -98,18 +101,22 @@ int main(int argc, char **argv)
98101
{ "dump-die-map", 0, &dump_die_map, 1 },
99102
{ "dump-types", 0, &dump_types, 1 },
100103
{ "dump-versions", 0, &dump_versions, 1 },
104+
{ "stable", 0, NULL, 's' },
101105
{ "symtypes", 1, NULL, 'T' },
102106
{ "help", 0, NULL, 'h' },
103107
{ 0, 0, NULL, 0 }
104108
};
105109

106-
while ((opt = getopt_long(argc, argv, "dT:h", opts, NULL)) != EOF) {
110+
while ((opt = getopt_long(argc, argv, "dsT:h", opts, NULL)) != EOF) {
107111
switch (opt) {
108112
case 0:
109113
break;
110114
case 'd':
111115
debug = 1;
112116
break;
117+
case 's':
118+
stable = 1;
119+
break;
113120
case 'T':
114121
symtypes = 1;
115122
symtypes_file = optarg;
@@ -150,6 +157,7 @@ int main(int argc, char **argv)
150157
strerror(errno));
151158

152159
symbol_read_symtab(fd);
160+
kabi_read_rules(fd);
153161

154162
dwfl = dwfl_begin(&callbacks);
155163
if (!dwfl)
@@ -166,6 +174,7 @@ int main(int argc, char **argv)
166174
error("dwfl_getmodules failed for '%s'", argv[n]);
167175

168176
dwfl_end(dwfl);
177+
kabi_free();
169178
}
170179

171180
if (symfile)

scripts/gendwarfksyms/gendwarfksyms.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ extern int dump_dies;
2424
extern int dump_die_map;
2525
extern int dump_types;
2626
extern int dump_versions;
27+
extern int stable;
2728
extern int symtypes;
2829

2930
/*
@@ -232,6 +233,7 @@ static inline bool cache_was_expanded(struct cache *cache, void *addr)
232233

233234
struct expansion_state {
234235
bool expand;
236+
const char *current_fqn;
235237
};
236238

237239
struct state {
@@ -263,4 +265,16 @@ void process_cu(Dwarf_Die *cudie);
263265

264266
void generate_symtypes_and_versions(FILE *file);
265267

268+
/*
269+
* kabi.c
270+
*/
271+
272+
bool kabi_is_enumerator_ignored(const char *fqn, const char *field);
273+
bool kabi_get_enumerator_value(const char *fqn, const char *field,
274+
unsigned long *value);
275+
bool kabi_is_declonly(const char *fqn);
276+
277+
void kabi_read_rules(int fd);
278+
void kabi_free(void);
279+
266280
#endif /* __GENDWARFKSYMS_H */

0 commit comments

Comments
 (0)