]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
gendwarfksyms: Add a kABI rule to override byte_size attributes
authorSami Tolvanen <samitolvanen@google.com>
Wed, 7 May 2025 23:14:06 +0000 (23:14 +0000)
committerMasahiro Yamada <masahiroy@kernel.org>
Sun, 25 May 2025 09:12:22 +0000 (18:12 +0900)
A data structure can be partially opaque to modules if its
allocation is handled by the core kernel, and modules only need
to access some of its members. In this situation, it's possible
to append new members to the structure without breaking the ABI,
as long as the layout for the original members remains unchanged.
For example, consider the following struct:

  struct s {
          unsigned long a;
          void *p;
  };

gendwarfksyms --stable --dump-dies produces the following type
expansion:

  variable structure_type s {
    member base_type long unsigned int byte_size(8) encoding(7) a
      data_member_location(0) ,
    member pointer_type {
      base_type void
    } byte_size(8) p data_member_location(8)
  } byte_size(16)

To append new members, we can use the KABI_IGNORE() macro to
hide them from gendwarfksyms --stable:

  struct s {
          /* old members with unchanged layout */
          unsigned long a;
          void *p;

          /* new members not accessed by modules */
          KABI_IGNORE(0, unsigned long n);
  };

However, we can't hide the fact that adding new members changes
the struct size, as seen in the updated type string:

  variable structure_type s {
    member base_type long unsigned int byte_size(8) encoding(7) a
      data_member_location(0) ,
    member pointer_type {
      base_type void
    } byte_size(8) p data_member_location(8)
  } byte_size(24)

In order to support this use case, add a kABI rule that makes it
possible to override the byte_size attribute for types:

  /*
   * struct s allocation is handled by the kernel, so
   * appending new members without changing the original
   * layout won't break the ABI.
   */
  KABI_BYTE_SIZE(s, 16);

This results in a type string that's unchanged from the original
and therefore, won't change versions for symbols that reference
the changed structure.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Petr Pavlu <petr.pavlu@suse.com>
Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
scripts/gendwarfksyms/dwarf.c
scripts/gendwarfksyms/examples/kabi.h
scripts/gendwarfksyms/examples/kabi_ex.c
scripts/gendwarfksyms/examples/kabi_ex.h
scripts/gendwarfksyms/gendwarfksyms.h
scripts/gendwarfksyms/kabi.c

index eed247d8abfcb93b7d4141766dbd31a6a712d1d3..13ea7bf1ae7dcdd208d25fe54655f1c0fb041907 100644 (file)
@@ -228,12 +228,24 @@ static void process_fqn(struct die *cache, Dwarf_Die *die)
 DEFINE_PROCESS_UDATA_ATTRIBUTE(accessibility)
 DEFINE_PROCESS_UDATA_ATTRIBUTE(alignment)
 DEFINE_PROCESS_UDATA_ATTRIBUTE(bit_size)
-DEFINE_PROCESS_UDATA_ATTRIBUTE(byte_size)
 DEFINE_PROCESS_UDATA_ATTRIBUTE(encoding)
 DEFINE_PROCESS_UDATA_ATTRIBUTE(data_bit_offset)
 DEFINE_PROCESS_UDATA_ATTRIBUTE(data_member_location)
 DEFINE_PROCESS_UDATA_ATTRIBUTE(discr_value)
 
+static void process_byte_size_attr(struct die *cache, Dwarf_Die *die)
+{
+       Dwarf_Word value;
+       unsigned long override;
+
+       if (get_udata_attr(die, DW_AT_byte_size, &value)) {
+               if (stable && kabi_get_byte_size(cache->fqn, &override))
+                       value = override;
+
+               process_fmt(cache, " byte_size(%" PRIu64 ")", value);
+       }
+}
+
 /* Match functions -- die_match_callback_t */
 #define DEFINE_MATCH(type)                                     \
        static bool match_##type##_type(Dwarf_Die *die)        \
index 97a5669b083d760ab9b69dedea14bf2212a6eb03..86f4428e047954071c9b302e2f74984d8ee6c56c 100644 (file)
 #define KABI_ENUMERATOR_VALUE(fqn, field, value) \
        __KABI_RULE(enumerator_value, fqn field, value)
 
+/*
+ * KABI_BYTE_SIZE(fqn, value)
+ *   Set the byte_size attribute for the struct/union/enum fqn to
+ *   value bytes.
+ */
+#define KABI_BYTE_SIZE(fqn, value) __KABI_RULE(byte_size, fqn, value)
+
 /*
  * KABI_RESERVE
  *   Reserve some "padding" in a structure for use by LTS backports.
index 0b7ffd830541d6ab57ce568ad8380f202711843b..b73ee5399a59ce2cfd2576d8f5ead7f0539f7663 100644 (file)
@@ -28,3 +28,5 @@ struct ex2c ex2c;
 struct ex3a ex3a;
 struct ex3b ex3b;
 struct ex3c ex3c;
+
+struct ex4a ex4a;
index 1736e0f65208143843684c16ca832eadde5e25a8..092c8cb7bcd7cc585c1285cdf835b6f60cf70590 100644 (file)
@@ -260,4 +260,26 @@ _Static_assert(sizeof(struct ex3a) == sizeof(struct ex3c), "ex3a size doesn't ma
  * STABLE-NEXT: } byte_size(16)
  */
 
+/*
+ * Example: An ignored field added to an end of a partially opaque struct,
+ * while keeping the byte_size attribute unchanged.
+ */
+
+struct ex4a {
+       unsigned long a;
+       KABI_IGNORE(0, unsigned long b);
+};
+
+/*
+ * This may be safe if the structure allocation is managed by the core kernel
+ * and the layout remains unchanged except for appended new members.
+ */
+KABI_BYTE_SIZE(ex4a, 8);
+
+/*
+ * STABLE:      variable structure_type ex4a {
+ * STABLE-NEXT:   member base_type [[ULONG]] byte_size(8) encoding(7) a data_member_location(0)
+ * STABLE-NEXT: } byte_size(8)
+ */
+
 #endif /* __KABI_EX_H__ */
index 2feec168bf732a479d52062994b4d9778af57165..2db49c2ad50e9f5aa69d968ba3e98042f84f6d6e 100644 (file)
@@ -287,6 +287,7 @@ void generate_symtypes_and_versions(FILE *file);
  * kabi.c
  */
 
+bool kabi_get_byte_size(const char *fqn, unsigned long *value);
 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);
index badf8d46b1540bd3bbf3157fb9fba3d04b651931..61620ff647bd18c683550d89540d941bd6b17eba 100644 (file)
  */
 #define KABI_RULE_TAG_ENUMERATOR_VALUE "enumerator_value"
 
+/*
+ * Rule: byte_size
+ * - For the fqn_field in the target field, set the byte_size
+ *   attribute to the value in the value field.
+ */
+#define KABI_RULE_TAG_BYTE_SIZE "byte_size"
+
 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,
 };
 
 #define RULE_HASH_BITS 7
@@ -127,6 +135,10 @@ void kabi_read_rules(int fd)
                        .type = KABI_RULE_TYPE_ENUMERATOR_VALUE,
                        .tag = KABI_RULE_TAG_ENUMERATOR_VALUE,
                },
+               {
+                       .type = KABI_RULE_TYPE_BYTE_SIZE,
+                       .tag = KABI_RULE_TAG_BYTE_SIZE,
+               },
        };
 
        if (!stable)
@@ -308,6 +320,19 @@ bool kabi_get_enumerator_value(const char *fqn, const char *field,
        return false;
 }
 
+bool kabi_get_byte_size(const char *fqn, unsigned long *value)
+{
+       struct rule *rule;
+
+       rule = find_rule(KABI_RULE_TYPE_BYTE_SIZE, fqn);
+       if (rule) {
+               *value = get_ulong_value(rule->value);
+               return true;
+       }
+
+       return false;
+}
+
 void kabi_free(void)
 {
        struct hlist_node *tmp;