lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <55549683-2453-4b67-a380-8ace34b3a06d@suse.com>
Date: Wed, 23 Oct 2024 16:53:46 +0200
From: Petr Pavlu <petr.pavlu@...e.com>
To: Sami Tolvanen <samitolvanen@...gle.com>
Cc: Masahiro Yamada <masahiroy@...nel.org>,
 Luis Chamberlain <mcgrof@...nel.org>, Miguel Ojeda <ojeda@...nel.org>,
 Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
 Matthew Maurer <mmaurer@...gle.com>, Alex Gaynor <alex.gaynor@...il.com>,
 Gary Guo <gary@...yguo.net>, Petr Pavlu <petr.pavlu@...e.com>,
 Daniel Gomez <da.gomez@...sung.com>, Neal Gompa <neal@...pa.dev>,
 Hector Martin <marcan@...can.st>, Janne Grunau <j@...nau.net>,
 Miroslav Benes <mbenes@...e.cz>, Asahi Linux <asahi@...ts.linux.dev>,
 Sedat Dilek <sedat.dilek@...il.com>, linux-kbuild@...r.kernel.org,
 linux-kernel@...r.kernel.org, linux-modules@...r.kernel.org,
 rust-for-linux@...r.kernel.org
Subject: Re: [PATCH v4 15/19] gendwarfksyms: Add support for reserved and
 ignored fields

On 10/8/24 20:38, Sami Tolvanen wrote:
> 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@...gle.com>
> Acked-by: Neal Gompa <neal@...pa.dev>
> ---
>  scripts/gendwarfksyms/dwarf.c             | 202 +++++++++++++++++++++-
>  scripts/gendwarfksyms/examples/kabi.h     |  80 +++++++++
>  scripts/gendwarfksyms/examples/kabi_ex0.c |  86 +++++++++
>  scripts/gendwarfksyms/examples/kabi_ex1.c |  89 ++++++++++
>  scripts/gendwarfksyms/examples/kabi_ex2.c |  98 +++++++++++
>  scripts/gendwarfksyms/gendwarfksyms.h     |  29 ++++
>  6 files changed, 583 insertions(+), 1 deletion(-)
>  create mode 100644 scripts/gendwarfksyms/examples/kabi_ex0.c
>  create mode 100644 scripts/gendwarfksyms/examples/kabi_ex1.c
>  create mode 100644 scripts/gendwarfksyms/examples/kabi_ex2.c
> 
> diff --git a/scripts/gendwarfksyms/dwarf.c b/scripts/gendwarfksyms/dwarf.c
> index b15f1a5db452..72e24140b6e3 100644
> --- a/scripts/gendwarfksyms/dwarf.c
> +++ b/scripts/gendwarfksyms/dwarf.c
> @@ -308,6 +308,9 @@ static void __process_list_type(struct state *state, struct die *cache,
>  {
>  	const char *name = get_name_attr(die);
>  
> +	if (stable && is_kabi_prefix(name))
> +		name = NULL;
> +
>  	process_list_comma(state, cache);
>  	process(cache, type);
>  	process_type_attr(state, cache, die);
> @@ -441,11 +444,193 @@ static void process_variant_part_type(struct state *state, struct die *cache,
>  	process(cache, "}");
>  }
>  
> +static int get_kabi_status(Dwarf_Die *die)
> +{
> +	const char *name = get_name_attr(die);
> +
> +	if (is_kabi_prefix(name)) {
> +		name += KABI_PREFIX_LEN;
> +
> +		if (!strncmp(name, KABI_RESERVED_PREFIX,
> +			     KABI_RESERVED_PREFIX_LEN))
> +			return KABI_RESERVED;
> +		if (!strncmp(name, KABI_IGNORED_PREFIX,
> +			     KABI_IGNORED_PREFIX_LEN))
> +			return KABI_IGNORED;
> +	}
> +
> +	return KABI_NORMAL;
> +}
> +
> +static int check_struct_member_kabi_status(struct state *state,
> +					   struct die *__unused, Dwarf_Die *die)
> +{
> +	int res;
> +
> +	if (dwarf_tag(die) != DW_TAG_member_type)
> +		error("expected a member");

Nit: If I understand the code correctly, the failed tag check here would
indicate an error in the internal logic that should never happen, so
a plain assert() would be more appropriate? Similarly in
check_union_member_kabi_status().

> +
> +	/*
> +	 * If the union member is a struct, expect the __kabi field to
> +	 * be the first member of the structure, i.e..:
> +	 *
> +	 * union {
> +	 * 	type new_member;
> +	 * 	struct {
> +	 * 		type __kabi_field;
> +	 * 	}
> +	 * };
> +	 */
> +	res = get_kabi_status(die);
> +
> +	if (res == KABI_RESERVED &&
> +	    !get_ref_die_attr(die, DW_AT_type, &state->kabi.placeholder))
> +		error("structure member missing a type?");
> +
> +	return res;
> +}
> +
> +static int check_union_member_kabi_status(struct state *state,
> +					  struct die *__unused, Dwarf_Die *die)
> +{
> +	Dwarf_Die type;
> +	int res;
> +
> +	if (dwarf_tag(die) != DW_TAG_member_type)
> +		error("expected a member");
> +
> +	if (!get_ref_die_attr(die, DW_AT_type, &type))
> +		error("union member missing a type?");
> +
> +	/*
> +	 * We expect a union with two members. Check if either of them
> +	 * has a __kabi name prefix, i.e.:
> +	 *
> +	 * union {
> +	 * 	...
> +	 * 	type memberN; // <- type, N = {0,1}
> +	 *	...
> +	 * };
> +	 *
> +	 * The member can also be a structure type, in which case we'll
> +	 * check the first structure member.
> +	 *
> +	 * In any case, stop processing after we've seen two members.
> +	 */
> +	res = get_kabi_status(die);
> +
> +	if (res == KABI_RESERVED)
> +		state->kabi.placeholder = type;
> +	if (res != KABI_NORMAL)
> +		return res;
> +
> +	if (dwarf_tag(&type) == DW_TAG_structure_type)
> +		res = checkp(process_die_container(
> +			state, NULL, &type, check_struct_member_kabi_status,
> +			match_member_type));
> +
> +	if (res <= KABI_NORMAL && ++state->kabi.members < 2)
> +		return 0; /* Continue */
> +
> +	return res;
> +}
> +
> +static int get_union_kabi_status(Dwarf_Die *die, Dwarf_Die *placeholder)
> +{
> +	struct state state;
> +	int res;
> +
> +	if (!stable)
> +		return KABI_NORMAL;
> +
> +	/*
> +	 * To maintain a stable kABI, distributions may choose to reserve
> +	 * space in structs for later use by adding placeholder members,
> +	 * for example:
> +	 *
> +	 * struct s {
> +	 * 	u32 a;
> +	 *	// an 8-byte placeholder for future use
> +	 * 	u64 __kabi_reserved_0;
> +	 * };
> +	 *
> +	 * When the reserved member is taken into use, the type change
> +	 * would normally cause the symbol version to change as well, but
> +	 * if the replacement uses the following convention, gendwarfksyms
> +	 * continues to use the placeholder type for versioning instead,
> +	 * thus maintaining the same symbol version:
> +	 *
> +	 * struct s {
> +	 * 	u32 a;
> +	 *	union {
> +	 * 		// placeholder replaced with a new member `b`
> +	 * 		struct t b;
> +	 * 		struct {
> +	 * 			// the placeholder type that is still
> +	 *			// used for versioning
> +	 * 			u64 __kabi_reserved_0;
> +	 * 		};
> +	 * 	};
> +	 * };
> +	 *
> +	 * I.e., as long as the replaced member is in a union, and the
> +	 * placeholder has a __kabi_reserved name prefix, we'll continue
> +	 * to use the placeholder type (here u64) for version calculation
> +	 * instead of the union type.
> +	 *
> +	 * It's also possible to ignore new members from versioning if
> +	 * they've been added to alignment holes, for example, by
> +	 * including them in a union with another member that uses the
> +	 * __kabi_ignored name prefix:
> +	 *
> +	 * struct s {
> +	 * 	u32 a;
> +	 *	// an alignment hole is used to add `n`
> +	 * 	union {
> +	 * 		u32 n;
> +	 *		// hide the entire union member from versioning
> +	 * 		u8 __kabi_ignored_0;
> +	 * 	};
> +	 * 	u64 b;
> +	 * };
> +	 *
> +	 * Note that the user of this feature is responsible for ensuring
> +	 * that the structure actually remains ABI compatible.
> +	 */
> +	state.kabi.members = 0;
> +
> +	res = checkp(process_die_container(&state, NULL, die,
> +					   check_union_member_kabi_status,
> +					   match_member_type));
> +
> +	if (placeholder && res == KABI_RESERVED)
> +		*placeholder = state.kabi.placeholder;
> +
> +	return res;
> +}
> +
> +static bool is_kabi_ignored(Dwarf_Die *die)
> +{
> +	Dwarf_Die type;
> +
> +	if (!stable)
> +		return false;
> +
> +	if (!get_ref_die_attr(die, DW_AT_type, &type))
> +		error("member missing a type?");
> +
> +	return dwarf_tag(&type) == DW_TAG_union_type &&
> +	       checkp(get_union_kabi_status(&type, NULL)) == KABI_IGNORED;
> +}
> +
>  static int ___process_structure_type(struct state *state, struct die *cache,
>  				     Dwarf_Die *die)
>  {
>  	switch (dwarf_tag(die)) {
>  	case DW_TAG_member:
> +		if (is_kabi_ignored(die))
> +			return 0;
> +		return check(process_type(state, cache, die));
>  	case DW_TAG_variant_part:
>  		return check(process_type(state, cache, die));
>  	case DW_TAG_class_type:
> @@ -503,7 +688,22 @@ static void __process_structure_type(struct state *state, struct die *cache,
>  
>  DEFINE_PROCESS_STRUCTURE_TYPE(class)
>  DEFINE_PROCESS_STRUCTURE_TYPE(structure)
> -DEFINE_PROCESS_STRUCTURE_TYPE(union)
> +
> +static void process_union_type(struct state *state, struct die *cache,
> +			       Dwarf_Die *die)
> +{
> +	Dwarf_Die placeholder;
> +
> +	int res = checkp(get_union_kabi_status(die, &placeholder));
> +
> +	if (res == KABI_RESERVED)
> +		check(process_type(state, cache, &placeholder));
> +	if (res > KABI_NORMAL)
> +		return;
> +
> +	__process_structure_type(state, cache, die, "union_type",
> +				 ___process_structure_type, match_all);
> +}
>  
>  static void process_enumerator_type(struct state *state, struct die *cache,
>  				    Dwarf_Die *die)
> diff --git a/scripts/gendwarfksyms/examples/kabi.h b/scripts/gendwarfksyms/examples/kabi.h
> index c53e8d4a7d2e..ec99c2fb9e96 100644
> --- a/scripts/gendwarfksyms/examples/kabi.h
> +++ b/scripts/gendwarfksyms/examples/kabi.h
> @@ -43,6 +43,28 @@
>  		__section(".discard.gendwarfksyms.kabi_rules") =     \
>  			"1\0" #hint "\0" #target "\0" #value
>  
> +#define __KABI_NORMAL_SIZE_ALIGN(_orig, _new)                                             \
> +	union {                                                                           \
> +		_Static_assert(                                                           \
> +			sizeof(struct { _new; }) <= sizeof(struct { _orig; }),            \
> +			__FILE__ ":" __stringify(__LINE__) ": " __stringify(              \
> +				_new) " is larger than " __stringify(_orig));             \
> +		_Static_assert(                                                           \
> +			__alignof__(struct { _new; }) <=                                  \
> +				__alignof__(struct { _orig; }),                           \
> +			__FILE__ ":" __stringify(__LINE__) ": " __stringify(              \
> +				_orig) " is not aligned the same as " __stringify(_new)); \
> +	}
> +
> +#define __KABI_REPLACE(_orig, _new)                    \
> +	union {                                        \
> +		_new;                                  \
> +		struct {                               \
> +			_orig;                         \
> +		};                                     \
> +		__KABI_NORMAL_SIZE_ALIGN(_orig, _new); \
> +	}
> +
>  /*
>   * KABI_USE_ARRAY(fqn)
>   *   Treat the struct fqn as a declaration, i.e. even if a definition
> @@ -58,4 +80,62 @@
>  #define KABI_ENUMERATOR_IGNORE(fqn, field) \
>  	__KABI_RULE(enumerator_ignore, fqn, field)
>  
> +/*
> + * KABI_RESERVE
> + *   Reserve some "padding" in a structure for use by LTS backports.
> + *   This normally placed at the end of a structure.

Nit: s/This normally/This is normally/.

> + *   number: the "number" of the padding variable in the structure.  Start with
> + *   1 and go up.
> + */
> +#define KABI_RESERVE(n) unsigned long __kabi_reserved##n
> +
> +/*
> + * KABI_RESERVE_ARRAY
> + *   Same as _BACKPORT_RESERVE but allocates an array with the specified
> + *   size in bytes.
> + */
> +#define KABI_RESERVE_ARRAY(n, s) \
> +	unsigned char __aligned(8) __kabi_reserved##n[s]
> +
> +/*
> + * KABI_IGNORE
> + *   Add a new field that's ignored in versioning.
> + */
> +#define KABI_IGNORE(n, _new)                     \
> +	union {                                  \
> +		_new;                            \
> +		unsigned char __kabi_ignored##n; \
> +	}
> +
> +/*
> + * KABI_USE(number, _new)
> + *   Use a previous padding entry that was defined with KABI_RESERVE
> + *   number: the previous "number" of the padding variable
> + *   _new: the variable to use now instead of the padding variable
> + */
> +#define KABI_USE(number, _new) __KABI_REPLACE(KABI_RESERVE(number), _new)
> +
> +/*
> + * KABI_USE2(number, _new1, _new2)
> + *   Use a previous padding entry that was defined with KABI_RESERVE for
> + *   two new variables that fit into 64 bits.  This is good for when you do not
> + *   want to "burn" a 64bit padding variable for a smaller variable size if not
> + *   needed.
> + */
> +#define KABI_USE2(number, _new1, _new2)        \
> +	__KABI_REPLACE(                        \
> +		KABI_RESERVE(number), struct { \
> +			_new1;                 \
> +			_new2;                 \
> +		})
> +/*
> + * KABI_USE_ARRAY(number, bytes, _new)
> + *   Use a previous padding entry that was defined with KABI_RESERVE_ARRAY
> + *   number: the previous "number" of the padding variable
> + *   bytes: the size in bytes reserved for the array
> + *   _new: the variable to use now instead of the padding variable
> + */
> +#define KABI_USE_ARRAY(number, bytes, _new) \
> +	__KABI_REPLACE(KABI_RESERVE_ARRAY(number, bytes), _new)
> +
>  #endif /* __KABI_H__ */
> diff --git a/scripts/gendwarfksyms/examples/kabi_ex0.c b/scripts/gendwarfksyms/examples/kabi_ex0.c
> new file mode 100644
> index 000000000000..934324cba837
> --- /dev/null
> +++ b/scripts/gendwarfksyms/examples/kabi_ex0.c
> @@ -0,0 +1,86 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * kabi_ex0.c
> + *
> + * Copyright (C) 2024 Google LLC
> + *
> + * Reserved and ignored data structure field examples with --stable.
> + */
> +
> +/*
> + * The comments below each example contain the expected gendwarfksyms
> + * output, which can be verified using LLVM's FileCheck tool:
> + *
> + * https://llvm.org/docs/CommandGuide/FileCheck.html
> + *
> + * $ gcc -g -c examples/kabi_ex0.c examples/kabi_ex0.o
> + *
> + * Verify --stable output:
> + *
> + * $ echo -e "ex0a\nex0b\nex0c" | \
> + * 	./gendwarfksyms --stable --dump-dies \
> + * 		examples/kabi_ex0.o 2>&1 >/dev/null | \
> + * 	FileCheck examples/kabi_ex0.c --check-prefix=STABLE
> + *
> + * Verify that symbol versions match with --stable:
> + *
> + * $ echo -e "ex0a\nex0b\nex0c" | \
> + * 	./gendwarfksyms --stable examples/kabi_ex0.o | \
> + * 	sort | \
> + * 	FileCheck examples/kabi_ex0.c --check-prefix=VERSION
> + */
> +
> +#include "kabi.h"
> +
> +/*
> + * Example 0: Reserved fields.
> + */
> +
> +struct {
> +	int a;
> +	KABI_RESERVE(0);
> +	KABI_RESERVE(1);
> +} ex0a;
> +
> +/*
> + * STABLE:      variable structure_type {
> + * STABLE-NEXT:   member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
> + * STABLE-NEXT:   member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) data_member_location(8) ,
> + * STABLE-NEXT:   member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(16)
> + * STABLE-NEXT: } byte_size(24)
> + *
> + * VERSION-DAG: #SYMVER ex0a 0x[[#%.08x,EX0:]]
> + */
> +
> +struct {
> +	int a;
> +	KABI_RESERVE(0);
> +	KABI_USE2(1, int b, int c);
> +} ex0b;
> +
> +/*
> + * STABLE:      variable structure_type {
> + * STABLE-NEXT:   member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
> + * STABLE-NEXT:   member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(8) ,
> + * STABLE-NEXT:   member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(16)
> + *
> + * STABLE-NEXT: } byte_size(24)
> + *
> + * VERSION-DAG: #SYMVER ex0b 0x[[#%.08x,EX0]]
> + */
> +
> +struct {
> +	int a;
> +	KABI_USE(0, void *p);
> +	KABI_USE2(1, int b, int c);
> +} ex0c;
> +
> +/*
> + * STABLE:      variable structure_type {
> + * STABLE-NEXT:   member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
> + * STABLE-NEXT:   member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(8) ,
> + * STABLE-NEXT:   member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(16)
> + * STABLE-NEXT: } byte_size(24)
> + *
> + * VERSION-DAG: #SYMVER ex0c 0x[[#%.08x,EX0]]
> + */
> diff --git a/scripts/gendwarfksyms/examples/kabi_ex1.c b/scripts/gendwarfksyms/examples/kabi_ex1.c
> new file mode 100644
> index 000000000000..7bc34bc7dec8
> --- /dev/null
> +++ b/scripts/gendwarfksyms/examples/kabi_ex1.c
> @@ -0,0 +1,89 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * kabi_ex1.c
> + *
> + * Copyright (C) 2024 Google LLC
> + *
> + * Reserved and ignored data structure field examples with --stable.
> + */
> +
> +/*
> + * The comments below each example contain the expected gendwarfksyms
> + * output, which can be verified using LLVM's FileCheck tool:
> + *
> + * https://llvm.org/docs/CommandGuide/FileCheck.html
> + *
> + * $ gcc -g -c examples/kabi_ex1.c examples/kabi_ex1.o
> + *
> + * Verify --stable output:
> + *
> + * $ echo -e "ex1a\nex1b\nex1c" | \
> + * 	./gendwarfksyms --stable --dump-dies \
> + * 		examples/kabi_ex1.o 2>&1 >/dev/null | \
> + * 	FileCheck examples/kabi_ex1.c --check-prefix=STABLE
> + *
> + * Verify that symbol versions match with --stable:
> + *
> + * $ echo -e "ex1a\nex1b\nex1c" | \
> + * 	./gendwarfksyms --stable examples/kabi_ex1.o | \
> + * 	sort | \
> + * 	FileCheck examples/kabi_ex1.c --check-prefix=VERSION
> + */
> +
> +#include "kabi.h"
> +
> +/*
> + * Example 1: A reserved array.
> + */
> +
> +struct {
> +	unsigned int a;
> +	KABI_RESERVE_ARRAY(0, 64);
> +} ex1a;
> +
> +/*
> + * STABLE:      variable structure_type {
> + * STABLE-NEXT:   member base_type unsigned int byte_size(4) encoding(7) a data_member_location(0) ,
> + * STABLE-NEXT:   member array_type[64] {
> + * STABLE-NEXT:     base_type unsigned char byte_size(1) encoding(8)
> + * STABLE-NEXT:   } data_member_location(8)
> + * STABLE-NEXT: } byte_size(72)
> + *
> + * VERSION-DAG: #SYMVER ex1a 0x[[#%.08x,EX1:]]
> + */
> +
> +struct {
> +	unsigned int a;
> +	KABI_USE_ARRAY(
> +		0, 64, struct {
> +			void *p;
> +			KABI_RESERVE_ARRAY(1, 56);
> +		});
> +} ex1b;
> +
> +/*
> + * STABLE:      variable structure_type {
> + * STABLE-NEXT:   member base_type unsigned int byte_size(4) encoding(7) a data_member_location(0) ,
> + * STABLE-NEXT:   member array_type[64] {
> + * STABLE-NEXT:     base_type unsigned char byte_size(1) encoding(8)
> + * STABLE-NEXT:   } data_member_location(8)
> + * STABLE-NEXT: } byte_size(72)
> + *
> + * VERSION-DAG: #SYMVER ex1b 0x[[#%.08x,EX1]]
> + */
> +
> +struct {
> +	unsigned int a;
> +	KABI_USE_ARRAY(0, 64, void *p[8]);
> +} ex1c;
> +
> +/*
> + * STABLE:      variable structure_type {
> + * STABLE-NEXT:   member base_type unsigned int byte_size(4) encoding(7) a data_member_location(0) ,
> + * STABLE-NEXT:   member array_type[64] {
> + * STABLE-NEXT:     base_type unsigned char byte_size(1) encoding(8)
> + * STABLE-NEXT:   } data_member_location(8)
> + * STABLE-NEXT: } byte_size(72)
> + *
> + * VERSION-DAG: #SYMVER ex1c 0x[[#%.08x,EX1]]
> + */
> diff --git a/scripts/gendwarfksyms/examples/kabi_ex2.c b/scripts/gendwarfksyms/examples/kabi_ex2.c
> new file mode 100644
> index 000000000000..947ea5675b4f
> --- /dev/null
> +++ b/scripts/gendwarfksyms/examples/kabi_ex2.c
> @@ -0,0 +1,98 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * kabi_ex2.c
> + *
> + * Copyright (C) 2024 Google LLC
> + *
> + * Reserved and ignored data structure field examples with --stable.
> + */
> +
> +/*
> + * The comments below each example contain the expected gendwarfksyms
> + * output, which can be verified using LLVM's FileCheck tool:
> + *
> + * https://llvm.org/docs/CommandGuide/FileCheck.html
> + *
> + * $ gcc -g -c examples/kabi_ex2.c examples/kabi_ex2.o
> + *
> + * Verify --stable output:
> + *
> + * $ echo -e "ex2a\nex2b\nex2c" | \
> + * 	./gendwarfksyms --stable --dump-dies \
> + * 		examples/kabi_ex2.o 2>&1 >/dev/null | \
> + * 	FileCheck examples/kabi_ex2.c --check-prefix=STABLE
> + *
> + * Verify that symbol versions match with --stable:
> + *
> + * $ echo -e "ex2a\nex2b\nex2c" | \
> + * 	./gendwarfksyms --stable examples/kabi_ex2.o | \
> + * 	sort | \
> + * 	FileCheck examples/kabi_ex2.c --check-prefix=VERSION
> + */
> +
> +#include "kabi.h"
> +
> +/*
> + * Example 2: An ignored field added to an alignment hole.
> + */
> +
> +struct {
> +	int a;
> +	unsigned long b;
> +	int c;
> +	unsigned long d;
> +} ex2a;
> +
> +/*
> + * STABLE:      variable structure_type {
> + * STABLE-NEXT:   member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
> + * STABLE-NEXT:   member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) b data_member_location(8)
> + * STABLE-NEXT:   member base_type int byte_size(4) encoding(5) c data_member_location(16) ,
> + * STABLE-NEXT:   member base_type [[ULONG]] byte_size(8) encoding(7) d data_member_location(24)
> + * STABLE-NEXT: } byte_size(32)
> + *
> + * VERSION-DAG: #SYMVER ex2a 0x[[#%.08x,EX2:]]
> + */
> +
> +struct {
> +	int a;
> +	KABI_IGNORE(0, unsigned int n);
> +	unsigned long b;
> +	int c;
> +	unsigned long d;
> +} ex2b;
> +
> +_Static_assert(sizeof(ex2a) == sizeof(ex2b), "ex2a size doesn't match ex2b");
> +
> +/*
> + * STABLE:      variable structure_type {
> + * STABLE-NEXT:   member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
> + * STABLE-NEXT:   member base_type [[ULONG]] byte_size(8) encoding(7) b data_member_location(8)
> + * STABLE-NEXT:   member base_type int byte_size(4) encoding(5) c data_member_location(16) ,
> + * STABLE-NEXT:   member base_type [[ULONG]] byte_size(8) encoding(7) d data_member_location(24)
> + * STABLE-NEXT: } byte_size(32)
> + *
> + * VERSION-DAG: #SYMVER ex2b 0x[[#%.08x,EX2]]
> + */
> +
> +struct {
> +	int a;
> +	KABI_IGNORE(0, unsigned int n);
> +	unsigned long b;
> +	int c;
> +	KABI_IGNORE(1, unsigned int m);
> +	unsigned long d;
> +} ex2c;
> +
> +_Static_assert(sizeof(ex2a) == sizeof(ex2c), "ex2a size doesn't match ex2c");
> +
> +/*
> + * STABLE:      variable structure_type {
> + * STABLE-NEXT:   member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
> + * STABLE-NEXT:   member base_type [[ULONG]] byte_size(8) encoding(7) b data_member_location(8)
> + * STABLE-NEXT:   member base_type int byte_size(4) encoding(5) c data_member_location(16) ,
> + * STABLE-NEXT:   member base_type [[ULONG]] byte_size(8) encoding(7) d data_member_location(24)
> + * STABLE-NEXT: } byte_size(32)
> + *
> + * VERSION-DAG: #SYMVER ex2c 0x[[#%.08x,EX2]]
> + */
> diff --git a/scripts/gendwarfksyms/gendwarfksyms.h b/scripts/gendwarfksyms/gendwarfksyms.h
> index f32ad4389b58..1cff868bacdb 100644
> --- a/scripts/gendwarfksyms/gendwarfksyms.h
> +++ b/scripts/gendwarfksyms/gendwarfksyms.h
> @@ -222,6 +222,20 @@ void cache_clear_expanded(struct expansion_cache *ec);
>  /*
>   * dwarf.c
>   */
> +
> +/* See dwarf.c:get_union_kabi_status */
> +#define KABI_PREFIX "__kabi_"
> +#define KABI_PREFIX_LEN (sizeof(KABI_PREFIX) - 1)
> +#define KABI_RESERVED_PREFIX "reserved"
> +#define KABI_RESERVED_PREFIX_LEN (sizeof(KABI_RESERVED_PREFIX) - 1)
> +#define KABI_IGNORED_PREFIX "ignored"
> +#define KABI_IGNORED_PREFIX_LEN (sizeof(KABI_IGNORED_PREFIX) - 1)
> +
> +static inline bool is_kabi_prefix(const char *name)
> +{
> +	return name && !strncmp(name, KABI_PREFIX, KABI_PREFIX_LEN);
> +}
> +

Nit: The new KABI_* macros and the is_kabi_prefix() function are used
only in dwarf.c so could be all moved there.

>  struct expansion_state {
>  	bool expand;
>  	unsigned int ptr_depth;
> @@ -229,6 +243,18 @@ struct expansion_state {
>  	const char *current_fqn;
>  };
>  
> +enum kabi_status {
> +	/* >0 to stop DIE processing */
> +	KABI_NORMAL = 1,
> +	KABI_RESERVED,
> +	KABI_IGNORED,
> +};
> +
> +struct kabi_state {
> +	int members;
> +	Dwarf_Die placeholder;
> +};
> +
>  struct state {
>  	struct symbol *sym;
>  	Dwarf_Die die;
> @@ -239,6 +265,9 @@ struct state {
>  	/* Structure expansion */
>  	struct expansion_state expand;
>  	struct expansion_cache expansion_cache;
> +
> +	/* Reserved or ignored members */
> +	struct kabi_state kabi;
>  };
>  
>  typedef int (*die_callback_t)(struct state *state, struct die *cache,

I've noted some nits above which you might want to trivially address.
Otherwise this looks ok to me, feel free to add:

Reviewed-by: Petr Pavlu <petr.pavlu@...e.com>

-- 
Thanks,
Petr

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ