Commit c9083467 authored by Sami Tolvanen's avatar Sami Tolvanen Committed by Masahiro Yamada
Browse files

gendwarfksyms: Add a kABI rule to override type strings

In rare situations where distributions must make significant
changes to otherwise opaque data structures that have
inadvertently been included in the published ABI, keeping
symbol versions stable using the existing kABI macros can
become tedious.

For example, Android decided to switch to a newer io_uring
implementation in the 5.10 GKI kernel "to resolve a huge number
of potential, and known, problems with the codebase," requiring
"horrible hacks" with genksyms:

  "A number of the io_uring structures get used in other core
  kernel structures, only as "opaque" pointers, so there is
  not any real ABI breakage.  But, due to the visibility of
  the structures going away, the CRC values of many scheduler
  variables and functions were changed."
    -- https://r.android.com/2425293



While these specific changes probably could have been hidden
from gendwarfksyms using the existing kABI macros, this may not
always be the case.

Add a last resort kABI rule that allows distribution
maintainers to fully override a type string for a symbol or a
type. Also add a more informative error message in case we find
a non-existent type references when calculating versions.

Suggested-by: default avatarGiuliano Procida <gprocida@google.com>
Signed-off-by: default avatarSami Tolvanen <samitolvanen@google.com>
Reviewed-by: default avatarPetr Pavlu <petr.pavlu@suse.com>
Signed-off-by: default avatarMasahiro Yamada <masahiroy@kernel.org>
parent db59d74e
Loading
Loading
Loading
Loading
+12 −2
Original line number Diff line number Diff line
@@ -37,11 +37,14 @@
#define __stringify(x...) __stringify_1(x)
#endif

#define __KABI_RULE(hint, target, value)                             \
#define ___KABI_RULE(hint, target, value)                            \
	static const char __PASTE(__gendwarfksyms_rule_,             \
				  __COUNTER__)[] __used __aligned(1) \
		__section(".discard.gendwarfksyms.kabi_rules") =     \
			"1\0" #hint "\0" #target "\0" #value
			"1\0" #hint "\0" target "\0" value

#define __KABI_RULE(hint, target, value) \
	___KABI_RULE(hint, #target, #value)

#define __KABI_NORMAL_SIZE_ALIGN(_orig, _new)                                             \
	union {                                                                           \
@@ -96,6 +99,13 @@
 */
#define KABI_BYTE_SIZE(fqn, value) __KABI_RULE(byte_size, fqn, value)

/*
 * KABI_TYPE_STRING(type, str)
 *   For the given type, override the type string used in symtypes
 *   output and version calculation with str.
 */
#define KABI_TYPE_STRING(type, str) ___KABI_RULE(type_string, type, str)

/*
 * KABI_RESERVE
 *   Reserve some "padding" in a structure for use by LTS backports.
+5 −0
Original line number Diff line number Diff line
@@ -30,3 +30,8 @@ struct ex3b ex3b;
struct ex3c ex3c;

struct ex4a ex4a;

struct ex5a ex5a;
struct ex5b ex5b;

int ex6a;
+77 −2
Original line number Diff line number Diff line
@@ -21,6 +21,12 @@
 * 	./gendwarfksyms --stable --dump-dies \
 * 		examples/kabi_ex.o 2>&1 >/dev/null | \
 * 	FileCheck examples/kabi_ex.h --check-prefix=STABLE

 * $ nm examples/kabi_ex.o | awk '{ print $NF }' | \
 * 	./gendwarfksyms --stable --dump-versions \
 * 		examples/kabi_ex.o 2>&1 >/dev/null | \
 * 	sort | \
 * 	FileCheck examples/kabi_ex.h --check-prefix=VERSIONS
 */

#ifndef __KABI_EX_H__
@@ -170,7 +176,7 @@ struct ex2a {
/*
 * STABLE:      variable structure_type ex2a {
 * 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 [[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)
@@ -227,7 +233,7 @@ struct ex3a {

/*
 * STABLE:      variable structure_type ex3a {
 * STABLE-NEXT:   member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) a data_member_location(0)
 * STABLE-NEXT:   member base_type [[ULONG]] byte_size(8) encoding(7) a data_member_location(0)
 * STABLE-NEXT:   member base_type [[ULONG]] byte_size(8) encoding(7) unused data_member_location(8)
 * STABLE-NEXT: } byte_size(16)
 */
@@ -282,4 +288,73 @@ KABI_BYTE_SIZE(ex4a, 8);
 * STABLE-NEXT: } byte_size(8)
 */

/*
 * Example: A type string override.
 */

struct ex5a {
	unsigned long a;
};

/*
 * This may be safe if the structure is fully opaque to modules, even though
 * its definition has inadvertently become part of the ABI.
 */
KABI_TYPE_STRING(
	"s#ex5a",
	"structure_type ex5a { member pointer_type { s#ex4a } byte_size(8) p data_member_location(0) } byte_size(8)");

/*
 * Make sure the fully expanded type string includes ex4a.
 *
 * VERSIONS:      ex5a variable structure_type ex5a {
 * VERSIONS-SAME:   member pointer_type {
 * VERSIONS-SAME:     structure_type ex4a {
 * VERSIONS-SAME:       member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) a data_member_location(0)
 * VERSIONS-SAME:     } byte_size(8)
 * VERSIONS-SAME:   } byte_size(8) p data_member_location(0)
 * VERSIONS-SAME: } byte_size(8)
 */

/*
 * Example: A type string definition for a non-existent type.
 */

struct ex5b {
	unsigned long a;
};

/* Replace the type string for struct ex5b */
KABI_TYPE_STRING(
	"s#ex5b",
	"structure_type ex5b { member pointer_type { s#ex5c } byte_size(8) p data_member_location(0) } byte_size(8)");

/* Define a type string for a non-existent struct ex5c */
KABI_TYPE_STRING(
	"s#ex5c",
	"structure_type ex5c { member base_type int byte_size(4) encoding(5) n data_member_location(0) } byte_size(8)");

/*
 * Make sure the fully expanded type string includes the definition for ex5c.
 *
 * VERSIONS:      ex5b variable structure_type ex5b {
 * VERSIONS-SAME:   member pointer_type {
 * VERSIONS-SAME:     structure_type ex5c {
 * VERSIONS-SAME:       member base_type int byte_size(4) encoding(5) n data_member_location(0)
 * VERSIONS-SAME:     } byte_size(8)
 * VERSIONS-SAME:   } byte_size(8) p data_member_location(0)
 * VERSIONS-SAME: } byte_size(8)
 */

/*
 * Example: A type string override for a symbol.
 */

KABI_TYPE_STRING("ex6a", "variable s#ex5c");

/*
 * VERSIONS:      ex6a variable structure_type ex5c {
 * VERSIONS-SAME:   member base_type int byte_size(4) encoding(5) n data_member_location(0)
 * VERSIONS-SAME: } byte_size(8)
 */
#endif /* __KABI_EX_H__ */
+1 −0
Original line number Diff line number Diff line
@@ -292,6 +292,7 @@ bool kabi_is_enumerator_ignored(const char *fqn, const char *field);
bool kabi_get_enumerator_value(const char *fqn, const char *field,
			       unsigned long *value);
bool kabi_is_declonly(const char *fqn);
bool kabi_get_type_string(const char *type, const char **str);

void kabi_read_rules(int fd);
void kabi_free(void);
+25 −0
Original line number Diff line number Diff line
@@ -61,12 +61,20 @@
 */
#define KABI_RULE_TAG_BYTE_SIZE "byte_size"

/*
 * Rule: type_string
 * - For the type reference in the fqn field, use the type string
 *   in the value field.
 */
#define KABI_RULE_TAG_TYPE_STRING "type_string"

enum kabi_rule_type {
	KABI_RULE_TYPE_UNKNOWN,
	KABI_RULE_TYPE_DECLONLY,
	KABI_RULE_TYPE_ENUMERATOR_IGNORE,
	KABI_RULE_TYPE_ENUMERATOR_VALUE,
	KABI_RULE_TYPE_BYTE_SIZE,
	KABI_RULE_TYPE_TYPE_STRING,
};

#define RULE_HASH_BITS 7
@@ -139,6 +147,10 @@ void kabi_read_rules(int fd)
			.type = KABI_RULE_TYPE_BYTE_SIZE,
			.tag = KABI_RULE_TAG_BYTE_SIZE,
		},
		{
			.type = KABI_RULE_TYPE_TYPE_STRING,
			.tag = KABI_RULE_TAG_TYPE_STRING,
		},
	};

	if (!stable)
@@ -333,6 +345,19 @@ bool kabi_get_byte_size(const char *fqn, unsigned long *value)
	return false;
}

bool kabi_get_type_string(const char *type, const char **str)
{
	struct rule *rule;

	rule = find_rule(KABI_RULE_TYPE_TYPE_STRING, type);
	if (rule) {
		*str = rule->value;
		return true;
	}

	return false;
}

void kabi_free(void)
{
	struct hlist_node *tmp;
Loading