From 6b91ff002c67b2502009f99115c4b7fe5b7b8248 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 2 May 2025 16:12:05 +0200 Subject: modpost: Use for() loop Slight cleanup by using a for() loop instead of while(). This makes it clearer what is the iteration and what is the actual work done. Signed-off-by: Peter Zijlstra Signed-off-by: Masahiro Yamada --- scripts/mod/modpost.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index be89921d60b6..2d1c059bf6cf 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1595,12 +1595,10 @@ static void read_symbols(const char *modname) license = get_next_modinfo(&info, "license", license); } - namespace = get_modinfo(&info, "import_ns"); - while (namespace) { + for (namespace = get_modinfo(&info, "import_ns"); + namespace; + namespace = get_next_modinfo(&info, "import_ns", namespace)) add_namespace(&mod->imported_namespaces, namespace); - namespace = get_next_modinfo(&info, "import_ns", - namespace); - } if (!get_modinfo(&info, "description")) warn("missing MODULE_DESCRIPTION() in %s\n", modname); -- cgit v1.2.3-59-g8ed1b From 520b1a147d918eb07132c847ad5598a17d3ff7ca Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 2 May 2025 16:12:06 +0200 Subject: module: Add module specific symbol namespace support Designate the "module:${modname}" symbol namespace to mean: 'only export to the named module'. Notably, explicit imports of anything in the "module:" space is forbidden. Signed-off-by: Peter Zijlstra Reviewed-by: Petr Pavlu Signed-off-by: Masahiro Yamada --- kernel/module/main.c | 33 +++++++++++++++++++++++++++++++-- scripts/mod/modpost.c | 11 ++++++++++- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/kernel/module/main.c b/kernel/module/main.c index a2859dc3eea6..5c5e725597e1 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -1083,6 +1083,14 @@ static char *get_modinfo(const struct load_info *info, const char *tag) return get_next_modinfo(info, tag, NULL); } +static bool verify_module_namespace(const char *namespace, const char *modname) +{ + const char *prefix = "module:"; + + return strstarts(namespace, prefix) && + !strcmp(namespace + strlen(prefix), modname); +} + static int verify_namespace_is_imported(const struct load_info *info, const struct kernel_symbol *sym, struct module *mod) @@ -1092,6 +1100,10 @@ static int verify_namespace_is_imported(const struct load_info *info, namespace = kernel_symbol_namespace(sym); if (namespace && namespace[0]) { + + if (verify_module_namespace(namespace, mod->name)) + return 0; + for_each_modinfo_entry(imported_namespace, info, "import_ns") { if (strcmp(namespace, imported_namespace) == 0) return 0; @@ -1659,15 +1671,30 @@ static void module_license_taint_check(struct module *mod, const char *license) } } -static void setup_modinfo(struct module *mod, struct load_info *info) +static int setup_modinfo(struct module *mod, struct load_info *info) { const struct module_attribute *attr; + char *imported_namespace; int i; for (i = 0; (attr = modinfo_attrs[i]); i++) { if (attr->setup) attr->setup(mod, get_modinfo(info, attr->attr.name)); } + + for_each_modinfo_entry(imported_namespace, info, "import_ns") { + /* + * 'module:' prefixed namespaces are implicit, disallow + * explicit imports. + */ + if (strstarts(imported_namespace, "module:")) { + pr_err("%s: module tries to import module namespace: %s\n", + mod->name, imported_namespace); + return -EPERM; + } + } + + return 0; } static void free_modinfo(struct module *mod) @@ -3335,7 +3362,9 @@ static int load_module(struct load_info *info, const char __user *uargs, goto free_unload; /* Set up MODINFO_ATTR fields */ - setup_modinfo(mod, info); + err = setup_modinfo(mod, info); + if (err) + goto free_modinfo; /* Fix up syms, so that st_value is a pointer to location. */ err = simplify_symbols(mod, info); diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 2d1c059bf6cf..c9ff4db26edb 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1682,6 +1682,14 @@ void buf_write(struct buffer *buf, const char *s, int len) buf->pos += len; } +static bool verify_module_namespace(const char *namespace, const char *modname) +{ + const char *prefix = "module:"; + + return strstarts(namespace, prefix) && + !strcmp(namespace + strlen(prefix), modname); +} + static void check_exports(struct module *mod) { struct symbol *s, *exp; @@ -1709,7 +1717,8 @@ static void check_exports(struct module *mod) basename = get_basename(mod->name); - if (!contains_namespace(&mod->imported_namespaces, exp->namespace)) { + if (!verify_module_namespace(exp->namespace, basename) && + !contains_namespace(&mod->imported_namespaces, exp->namespace)) { modpost_log(!allow_missing_ns_imports, "module %s uses symbol %s from namespace %s, but does not import it.\n", basename, exp->name, exp->namespace); -- cgit v1.2.3-59-g8ed1b From 754f8733fc09dd92093cfbd34fa71a42d152b250 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 2 May 2025 16:12:07 +0200 Subject: module: Extend the module namespace parsing Instead of only accepting "module:${name}", extend it with a comma separated list of module names and add tail glob support. That is, something like: "module:foo-*,bar" is now possible. Signed-off-by: Peter Zijlstra Reviewed-by: Petr Pavlu Signed-off-by: Masahiro Yamada --- kernel/module/main.c | 36 ++++++++++++++++++++++++++++++++++-- scripts/mod/modpost.c | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/kernel/module/main.c b/kernel/module/main.c index 5c5e725597e1..e4b6968dc308 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -1083,12 +1083,44 @@ static char *get_modinfo(const struct load_info *info, const char *tag) return get_next_modinfo(info, tag, NULL); } +/** + * verify_module_namespace() - does @modname have access to this symbol's @namespace + * @namespace: export symbol namespace + * @modname: module name + * + * If @namespace is prefixed with "module:" to indicate it is a module namespace + * then test if @modname matches any of the comma separated patterns. + * + * The patterns only support tail-glob. + */ static bool verify_module_namespace(const char *namespace, const char *modname) { + size_t len, modlen = strlen(modname); const char *prefix = "module:"; + const char *sep; + bool glob; + + if (!strstarts(namespace, prefix)) + return false; + + for (namespace += strlen(prefix); *namespace; namespace = sep) { + sep = strchrnul(namespace, ','); + len = sep - namespace; - return strstarts(namespace, prefix) && - !strcmp(namespace + strlen(prefix), modname); + glob = false; + if (sep[-1] == '*') { + len--; + glob = true; + } + + if (*sep) + sep++; + + if (strncmp(namespace, modname, len) == 0 && (glob || len == modlen)) + return true; + } + + return false; } static int verify_namespace_is_imported(const struct load_info *info, diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index c9ff4db26edb..16a69a129805 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1682,12 +1682,44 @@ void buf_write(struct buffer *buf, const char *s, int len) buf->pos += len; } +/** + * verify_module_namespace() - does @modname have access to this symbol's @namespace + * @namespace: export symbol namespace + * @modname: module name + * + * If @namespace is prefixed with "module:" to indicate it is a module namespace + * then test if @modname matches any of the comma separated patterns. + * + * The patterns only support tail-glob. + */ static bool verify_module_namespace(const char *namespace, const char *modname) { + size_t len, modlen = strlen(modname); const char *prefix = "module:"; + const char *sep; + bool glob; + + if (!strstarts(namespace, prefix)) + return false; + + for (namespace += strlen(prefix); *namespace; namespace = sep) { + sep = strchrnul(namespace, ','); + len = sep - namespace; - return strstarts(namespace, prefix) && - !strcmp(namespace + strlen(prefix), modname); + glob = false; + if (sep[-1] == '*') { + len--; + glob = true; + } + + if (*sep) + sep++; + + if (strncmp(namespace, modname, len) == 0 && (glob || len == modlen)) + return true; + } + + return false; } static void check_exports(struct module *mod) -- cgit v1.2.3-59-g8ed1b From 0267cbf297bf7ed9ef5181feceea9fe03c84318e Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 2 May 2025 16:12:08 +0200 Subject: module: Account for the build time module name mangling Sean noted that scripts/Makefile.lib:name-fix-token rule will mangle the module name with s/-/_/g. Since this happens late in the build, only the kernel needs to bother with this, the modpost tool still sees the original name. Reported-by: Sean Christopherson Signed-off-by: Peter Zijlstra Tested-by: Sean Christopherson Reviewed-by: Petr Pavlu Signed-off-by: Masahiro Yamada --- kernel/module/main.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/kernel/module/main.c b/kernel/module/main.c index e4b6968dc308..81035f6552ec 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -169,6 +169,30 @@ static inline void add_taint_module(struct module *mod, unsigned flag, set_bit(flag, &mod->taints); } +/* + * Like strncmp(), except s/-/_/g as per scripts/Makefile.lib:name-fix-token rule. + */ +static int mod_strncmp(const char *str_a, const char *str_b, size_t n) +{ + for (int i = 0; i < n; i++) { + char a = str_a[i]; + char b = str_b[i]; + int d; + + if (a == '-') a = '_'; + if (b == '-') b = '_'; + + d = a - b; + if (d) + return d; + + if (!a) + break; + } + + return 0; +} + /* * A thread that wants to hold a reference to a module only while it * is running can call this to safely exit. @@ -1116,7 +1140,7 @@ static bool verify_module_namespace(const char *namespace, const char *modname) if (*sep) sep++; - if (strncmp(namespace, modname, len) == 0 && (glob || len == modlen)) + if (mod_strncmp(namespace, modname, len) == 0 && (glob || len == modlen)) return true; } -- cgit v1.2.3-59-g8ed1b From 707f853d7fa3ce323a6875487890c213e34d81a0 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 2 May 2025 16:12:09 +0200 Subject: module: Provide EXPORT_SYMBOL_GPL_FOR_MODULES() helper Helper macro to more easily limit the export of a symbol to a given list of modules. Eg: EXPORT_SYMBOL_GPL_FOR_MODULES(preempt_notifier_inc, "kvm"); will limit the use of said function to kvm.ko, any other module trying to use this symbol will refure to load (and get modpost build failures). Requested-by: Masahiro Yamada Requested-by: Christoph Hellwig Signed-off-by: Peter Zijlstra Reviewed-by: Petr Pavlu Signed-off-by: Masahiro Yamada --- Documentation/core-api/symbol-namespaces.rst | 22 ++++++++++++++++++++++ include/linux/export.h | 12 ++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/Documentation/core-api/symbol-namespaces.rst b/Documentation/core-api/symbol-namespaces.rst index 06f766a6aab2..c6f59c5e2564 100644 --- a/Documentation/core-api/symbol-namespaces.rst +++ b/Documentation/core-api/symbol-namespaces.rst @@ -28,6 +28,9 @@ kernel. As of today, modules that make use of symbols exported into namespaces, are required to import the namespace. Otherwise the kernel will, depending on its configuration, reject loading the module or warn about a missing import. +Additionally, it is possible to put symbols into a module namespace, strictly +limiting which modules are allowed to use these symbols. + 2. How to define Symbol Namespaces ================================== @@ -83,6 +86,22 @@ unit as preprocessor statement. The above example would then read:: within the corresponding compilation unit before the #include for . Typically it's placed before the first #include statement. +2.3 Using the EXPORT_SYMBOL_GPL_FOR_MODULES() macro +=================================================== + +Symbols exported using this macro are put into a module namespace. This +namespace cannot be imported. + +The macro takes a comma separated list of module names, allowing only those +modules to access this symbol. Simple tail-globs are supported. + +For example: + + EXPORT_SYMBOL_GPL_FOR_MODULES(preempt_notifier_inc, "kvm,kvm-*") + +will limit usage of this symbol to modules whoes name matches the given +patterns. + 3. How to use Symbols exported in Namespaces ============================================ @@ -154,3 +173,6 @@ in-tree modules:: You can also run nsdeps for external module builds. A typical usage is:: $ make -C M=$PWD nsdeps + +Note: it will happily generate an import statement for the module namespace; +which will not work and generates build and runtime failures. diff --git a/include/linux/export.h b/include/linux/export.h index a8c23d945634..f35d03b4113b 100644 --- a/include/linux/export.h +++ b/include/linux/export.h @@ -24,11 +24,17 @@ .long sym #endif -#define ___EXPORT_SYMBOL(sym, license, ns) \ +/* + * LLVM integrated assembler cam merge adjacent string literals (like + * C and GNU-as) passed to '.ascii', but not to '.asciz' and chokes on: + * + * .asciz "MODULE_" "kvm" ; + */ +#define ___EXPORT_SYMBOL(sym, license, ns...) \ .section ".export_symbol","a" ASM_NL \ __export_symbol_##sym: ASM_NL \ .asciz license ASM_NL \ - .asciz ns ASM_NL \ + .ascii ns "\0" ASM_NL \ __EXPORT_SYMBOL_REF(sym) ASM_NL \ .previous @@ -85,4 +91,6 @@ #define EXPORT_SYMBOL_NS(sym, ns) __EXPORT_SYMBOL(sym, "", ns) #define EXPORT_SYMBOL_NS_GPL(sym, ns) __EXPORT_SYMBOL(sym, "GPL", ns) +#define EXPORT_SYMBOL_GPL_FOR_MODULES(sym, mods) __EXPORT_SYMBOL(sym, "GPL", "module:" mods) + #endif /* _LINUX_EXPORT_H */ -- cgit v1.2.3-59-g8ed1b From ff2c5f5a9e01b9fc3b4959c2b3f40843cc0a5ecb Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Wed, 7 May 2025 23:14:05 +0000 Subject: gendwarfksyms: Clean up kABI rule look-ups Reduce code duplication by moving kABI rule look-ups to separate functions. Signed-off-by: Sami Tolvanen Reviewed-by: Petr Pavlu Signed-off-by: Masahiro Yamada --- scripts/gendwarfksyms/kabi.c | 101 +++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 57 deletions(-) diff --git a/scripts/gendwarfksyms/kabi.c b/scripts/gendwarfksyms/kabi.c index 66f01fcd1607..badf8d46b154 100644 --- a/scripts/gendwarfksyms/kabi.c +++ b/scripts/gendwarfksyms/kabi.c @@ -222,33 +222,55 @@ void kabi_read_rules(int fd) check(elf_end(elf)); } -bool kabi_is_declonly(const char *fqn) +static char *get_enumerator_target(const char *fqn, const char *field) +{ + char *target = NULL; + + if (asprintf(&target, "%s %s", fqn, field) < 0) + error("asprintf failed for '%s %s'", fqn, field); + + return target; +} + +static struct rule *find_rule(enum kabi_rule_type type, const char *target) { struct rule *rule; if (!stable) - return false; - if (!fqn || !*fqn) - return false; + return NULL; + if (!target || !*target) + return NULL; hash_for_each_possible(rules, rule, hash, - rule_values_hash(KABI_RULE_TYPE_DECLONLY, fqn)) { - if (rule->type == KABI_RULE_TYPE_DECLONLY && - !strcmp(fqn, rule->target)) - return true; + rule_values_hash(type, target)) { + if (rule->type == type && !strcmp(target, rule->target)) + return rule; } - return false; + return NULL; } -static char *get_enumerator_target(const char *fqn, const char *field) +static struct rule *find_enumerator_rule(enum kabi_rule_type type, + const char *fqn, const char *field) { - char *target = NULL; + struct rule *rule; + char *target; - if (asprintf(&target, "%s %s", fqn, field) < 0) - error("asprintf failed for '%s %s'", fqn, field); + if (!stable) + return NULL; + if (!fqn || !*fqn || !field || !*field) + return NULL; - return target; + target = get_enumerator_target(fqn, field); + rule = find_rule(type, target); + + free(target); + return rule; +} + +bool kabi_is_declonly(const char *fqn) +{ + return !!find_rule(KABI_RULE_TYPE_DECLONLY, fqn); } static unsigned long get_ulong_value(const char *value) @@ -267,58 +289,23 @@ static unsigned long get_ulong_value(const char *value) bool kabi_is_enumerator_ignored(const char *fqn, const char *field) { - bool match = false; - struct rule *rule; - char *target; - - if (!stable) - return false; - if (!fqn || !*fqn || !field || !*field) - return false; - - target = get_enumerator_target(fqn, field); - - hash_for_each_possible( - rules, rule, hash, - rule_values_hash(KABI_RULE_TYPE_ENUMERATOR_IGNORE, target)) { - if (rule->type == KABI_RULE_TYPE_ENUMERATOR_IGNORE && - !strcmp(target, rule->target)) { - match = true; - break; - } - } - - free(target); - return match; + return !!find_enumerator_rule(KABI_RULE_TYPE_ENUMERATOR_IGNORE, fqn, + field); } bool kabi_get_enumerator_value(const char *fqn, const char *field, unsigned long *value) { - bool match = false; struct rule *rule; - char *target; - if (!stable) - return false; - if (!fqn || !*fqn || !field || !*field) - return false; - - target = get_enumerator_target(fqn, field); - - hash_for_each_possible(rules, rule, hash, - rule_values_hash(KABI_RULE_TYPE_ENUMERATOR_VALUE, - target)) { - if (rule->type == KABI_RULE_TYPE_ENUMERATOR_VALUE && - !strcmp(target, rule->target)) { - *value = get_ulong_value(rule->value); - match = true; - break; - } + rule = find_enumerator_rule(KABI_RULE_TYPE_ENUMERATOR_VALUE, fqn, + field); + if (rule) { + *value = get_ulong_value(rule->value); + return true; } - free(target); - return match; + return false; } void kabi_free(void) -- cgit v1.2.3-59-g8ed1b From db59d74e5da144111fc133fb1bf72e6392bdb04e Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Wed, 7 May 2025 23:14:06 +0000 Subject: gendwarfksyms: Add a kABI rule to override byte_size attributes 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 Reviewed-by: Petr Pavlu Signed-off-by: Masahiro Yamada --- scripts/gendwarfksyms/dwarf.c | 14 +++++++++++++- scripts/gendwarfksyms/examples/kabi.h | 7 +++++++ scripts/gendwarfksyms/examples/kabi_ex.c | 2 ++ scripts/gendwarfksyms/examples/kabi_ex.h | 22 ++++++++++++++++++++++ scripts/gendwarfksyms/gendwarfksyms.h | 1 + scripts/gendwarfksyms/kabi.c | 25 +++++++++++++++++++++++++ 6 files changed, 70 insertions(+), 1 deletion(-) diff --git a/scripts/gendwarfksyms/dwarf.c b/scripts/gendwarfksyms/dwarf.c index eed247d8abfc..13ea7bf1ae7d 100644 --- a/scripts/gendwarfksyms/dwarf.c +++ b/scripts/gendwarfksyms/dwarf.c @@ -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) \ diff --git a/scripts/gendwarfksyms/examples/kabi.h b/scripts/gendwarfksyms/examples/kabi.h index 97a5669b083d..86f4428e0479 100644 --- a/scripts/gendwarfksyms/examples/kabi.h +++ b/scripts/gendwarfksyms/examples/kabi.h @@ -89,6 +89,13 @@ #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. diff --git a/scripts/gendwarfksyms/examples/kabi_ex.c b/scripts/gendwarfksyms/examples/kabi_ex.c index 0b7ffd830541..b73ee5399a59 100644 --- a/scripts/gendwarfksyms/examples/kabi_ex.c +++ b/scripts/gendwarfksyms/examples/kabi_ex.c @@ -28,3 +28,5 @@ struct ex2c ex2c; struct ex3a ex3a; struct ex3b ex3b; struct ex3c ex3c; + +struct ex4a ex4a; diff --git a/scripts/gendwarfksyms/examples/kabi_ex.h b/scripts/gendwarfksyms/examples/kabi_ex.h index 1736e0f65208..092c8cb7bcd7 100644 --- a/scripts/gendwarfksyms/examples/kabi_ex.h +++ b/scripts/gendwarfksyms/examples/kabi_ex.h @@ -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__ */ diff --git a/scripts/gendwarfksyms/gendwarfksyms.h b/scripts/gendwarfksyms/gendwarfksyms.h index 2feec168bf73..2db49c2ad50e 100644 --- a/scripts/gendwarfksyms/gendwarfksyms.h +++ b/scripts/gendwarfksyms/gendwarfksyms.h @@ -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); diff --git a/scripts/gendwarfksyms/kabi.c b/scripts/gendwarfksyms/kabi.c index badf8d46b154..61620ff647bd 100644 --- a/scripts/gendwarfksyms/kabi.c +++ b/scripts/gendwarfksyms/kabi.c @@ -54,11 +54,19 @@ */ #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; -- cgit v1.2.3-59-g8ed1b From c9083467f7b97e7c06b7a9038c4f18095329bd37 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Wed, 7 May 2025 23:14:07 +0000 Subject: 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: Giuliano Procida Signed-off-by: Sami Tolvanen Reviewed-by: Petr Pavlu Signed-off-by: Masahiro Yamada --- scripts/gendwarfksyms/examples/kabi.h | 14 +++- scripts/gendwarfksyms/examples/kabi_ex.c | 5 ++ scripts/gendwarfksyms/examples/kabi_ex.h | 79 ++++++++++++++++- scripts/gendwarfksyms/gendwarfksyms.h | 1 + scripts/gendwarfksyms/kabi.c | 25 ++++++ scripts/gendwarfksyms/types.c | 140 +++++++++++++++++++++++++++---- 6 files changed, 246 insertions(+), 18 deletions(-) diff --git a/scripts/gendwarfksyms/examples/kabi.h b/scripts/gendwarfksyms/examples/kabi.h index 86f4428e0479..170733a3fba4 100644 --- a/scripts/gendwarfksyms/examples/kabi.h +++ b/scripts/gendwarfksyms/examples/kabi.h @@ -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. diff --git a/scripts/gendwarfksyms/examples/kabi_ex.c b/scripts/gendwarfksyms/examples/kabi_ex.c index b73ee5399a59..1f799eb7c756 100644 --- a/scripts/gendwarfksyms/examples/kabi_ex.c +++ b/scripts/gendwarfksyms/examples/kabi_ex.c @@ -30,3 +30,8 @@ struct ex3b ex3b; struct ex3c ex3c; struct ex4a ex4a; + +struct ex5a ex5a; +struct ex5b ex5b; + +int ex6a; diff --git a/scripts/gendwarfksyms/examples/kabi_ex.h b/scripts/gendwarfksyms/examples/kabi_ex.h index 092c8cb7bcd7..785b211d9c58 100644 --- a/scripts/gendwarfksyms/examples/kabi_ex.h +++ b/scripts/gendwarfksyms/examples/kabi_ex.h @@ -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__ */ diff --git a/scripts/gendwarfksyms/gendwarfksyms.h b/scripts/gendwarfksyms/gendwarfksyms.h index 2db49c2ad50e..7dd03ffe0c5c 100644 --- a/scripts/gendwarfksyms/gendwarfksyms.h +++ b/scripts/gendwarfksyms/gendwarfksyms.h @@ -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); diff --git a/scripts/gendwarfksyms/kabi.c b/scripts/gendwarfksyms/kabi.c index 61620ff647bd..b3ade713778f 100644 --- a/scripts/gendwarfksyms/kabi.c +++ b/scripts/gendwarfksyms/kabi.c @@ -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; diff --git a/scripts/gendwarfksyms/types.c b/scripts/gendwarfksyms/types.c index 6f37289104ff..39ce1770e463 100644 --- a/scripts/gendwarfksyms/types.c +++ b/scripts/gendwarfksyms/types.c @@ -100,7 +100,7 @@ static void type_expansion_append(struct type_expansion *type, const char *s, #define TYPE_HASH_BITS 12 static HASHTABLE_DEFINE(type_map, 1 << TYPE_HASH_BITS); -static int type_map_get(const char *name, struct type_expansion **res) +static int __type_map_get(const char *name, struct type_expansion **res) { struct type_expansion *e; @@ -114,11 +114,12 @@ static int type_map_get(const char *name, struct type_expansion **res) return -1; } -static void type_map_add(const char *name, struct type_expansion *type) +static struct type_expansion *type_map_add(const char *name, + struct type_expansion *type) { struct type_expansion *e; - if (type_map_get(name, &e)) { + if (__type_map_get(name, &e)) { e = xmalloc(sizeof(struct type_expansion)); type_expansion_init(e); e->name = xstrdup(name); @@ -130,7 +131,7 @@ static void type_map_add(const char *name, struct type_expansion *type) } else { /* Use the longest available expansion */ if (type->len <= e->len) - return; + return e; type_list_free(&e->expanded); @@ -148,6 +149,34 @@ static void type_map_add(const char *name, struct type_expansion *type) type_list_write(&e->expanded, stderr); checkp(fputs("\n", stderr)); } + + return e; +} + +static void type_parse(const char *name, const char *str, + struct type_expansion *type); + +static int type_map_get(const char *name, struct type_expansion **res) +{ + struct type_expansion type; + const char *override; + + if (!__type_map_get(name, res)) + return 0; + + /* + * If die_map didn't contain a type, we might still have + * a type_string kABI rule that defines it. + */ + if (stable && kabi_get_type_string(name, &override)) { + type_expansion_init(&type); + type_parse(name, override, &type); + *res = type_map_add(name, &type); + type_expansion_free(&type); + return 0; + } + + return -1; } static void type_map_write(FILE *file) @@ -267,15 +296,18 @@ static char *get_type_name(struct die *cache) return name; } -static void __calculate_version(struct version *version, struct list_head *list) +static void __calculate_version(struct version *version, + struct type_expansion *type) { struct type_list_entry *entry; struct type_expansion *e; /* Calculate a CRC over an expanded type string */ - list_for_each_entry(entry, list, list) { + list_for_each_entry(entry, &type->expanded, list) { if (is_type_prefix(entry->str)) { - check(type_map_get(entry->str, &e)); + if (type_map_get(entry->str, &e)) + error("unknown type reference to '%s' when expanding '%s'", + entry->str, type->name); /* * It's sufficient to expand each type reference just @@ -285,7 +317,7 @@ static void __calculate_version(struct version *version, struct list_head *list) version_add(version, entry->str); } else { cache_mark_expanded(&expansion_cache, e); - __calculate_version(version, &e->expanded); + __calculate_version(version, e); } } else { version_add(version, entry->str); @@ -293,10 +325,11 @@ static void __calculate_version(struct version *version, struct list_head *list) } } -static void calculate_version(struct version *version, struct list_head *list) +static void calculate_version(struct version *version, + struct type_expansion *type) { version_init(version); - __calculate_version(version, list); + __calculate_version(version, type); cache_free(&expansion_cache); } @@ -372,9 +405,80 @@ static void type_expand(struct die *cache, struct type_expansion *type, cache_free(&expansion_cache); } +static void type_parse(const char *name, const char *str, + struct type_expansion *type) +{ + char *fragment; + size_t start = 0; + size_t end; + size_t pos; + + if (!*str) + error("empty type string override for '%s'", name); + + type_expansion_init(type); + + for (pos = 0; str[pos]; ++pos) { + bool empty; + char marker = ' '; + + if (!is_type_prefix(&str[pos])) + continue; + + end = pos + 2; + + /* + * Find the end of the type reference. If the type name contains + * spaces, it must be in single quotes. + */ + if (str[end] == '\'') { + marker = '\''; + ++end; + } + while (str[end] && str[end] != marker) + ++end; + + /* Check that we have a non-empty type name */ + if (marker == '\'') { + if (str[end] != marker) + error("incomplete %c# type reference for '%s' (string : '%s')", + str[pos], name, str); + empty = end == pos + 3; + ++end; + } else { + empty = end == pos + 2; + } + if (empty) + error("empty %c# type name for '%s' (string: '%s')", + str[pos], name, str); + + /* Append the part of the string before the type reference */ + if (pos > start) { + fragment = xstrndup(&str[start], pos - start); + type_expansion_append(type, fragment, fragment); + } + + /* + * Append the type reference -- note that if the reference + * is invalid, i.e. points to a non-existent type, we will + * print out an error when calculating versions. + */ + fragment = xstrndup(&str[pos], end - pos); + type_expansion_append(type, fragment, fragment); + + start = end; + pos = end - 1; + } + + /* Append the rest of the type string, if there's any left */ + if (str[start]) + type_expansion_append(type, &str[start], NULL); +} + static void expand_type(struct die *cache, void *arg) { struct type_expansion type; + const char *override; char *name; if (cache->mapped) @@ -399,9 +503,13 @@ static void expand_type(struct die *cache, void *arg) return; debug("%s", name); - type_expand(cache, &type, true); - type_map_add(name, &type); + if (stable && kabi_get_type_string(name, &override)) + type_parse(name, override, &type); + else + type_expand(cache, &type, true); + + type_map_add(name, &type); type_expansion_free(&type); free(name); } @@ -410,6 +518,7 @@ static void expand_symbol(struct symbol *sym, void *arg) { struct type_expansion type; struct version version; + const char *override; struct die *cache; /* @@ -423,11 +532,14 @@ static void expand_symbol(struct symbol *sym, void *arg) if (__die_map_get(sym->die_addr, DIE_SYMBOL, &cache)) return; /* We'll warn about missing CRCs later. */ - type_expand(cache, &type, false); + if (stable && kabi_get_type_string(sym->name, &override)) + type_parse(sym->name, override, &type); + else + type_expand(cache, &type, false); /* If the symbol already has a version, don't calculate it again. */ if (sym->state != SYMBOL_PROCESSED) { - calculate_version(&version, &type.expanded); + calculate_version(&version, &type); symbol_set_crc(sym, version.crc); debug("%s = %lx", sym->name, version.crc); -- cgit v1.2.3-59-g8ed1b From 881bf900bc880ee3ad88c0dd74253785088a25d7 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Wed, 7 May 2025 23:14:08 +0000 Subject: Documentation/kbuild: Drop section numbers Change the gendwarfksyms documentation to use proper chapter, section, and subsection adornments instead of fragile section numbers. Suggested-by: Masahiro Yamada Signed-off-by: Sami Tolvanen Signed-off-by: Masahiro Yamada --- Documentation/kbuild/gendwarfksyms.rst | 44 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Documentation/kbuild/gendwarfksyms.rst b/Documentation/kbuild/gendwarfksyms.rst index e4beaae7e456..9694ec99d190 100644 --- a/Documentation/kbuild/gendwarfksyms.rst +++ b/Documentation/kbuild/gendwarfksyms.rst @@ -2,8 +2,8 @@ DWARF module versioning ======================= -1. Introduction -=============== +Introduction +============ When CONFIG_MODVERSIONS is enabled, symbol versions for modules are typically calculated from preprocessed source code using the @@ -14,8 +14,8 @@ selected, **gendwarfksyms** is used instead to calculate symbol versions from the DWARF debugging information, which contains the necessary details about the final module ABI. -1.1. Usage -========== +Usage +----- gendwarfksyms accepts a list of object files on the command line, and a list of symbol names (one per line) in standard input:: @@ -33,8 +33,8 @@ list of symbol names (one per line) in standard input:: -h, --help Print this message -2. Type information availability -================================ +Type information availability +============================= While symbols are typically exported in the same translation unit (TU) where they're defined, it's also perfectly fine for a TU to export @@ -56,8 +56,8 @@ type for calculating symbol versions even if the symbol is defined elsewhere. The name of the symbol pointer is expected to start with `__gendwarfksyms_ptr_`, followed by the name of the exported symbol. -3. Symtypes output format -========================= +Symtypes output format +====================== Similarly to genksyms, gendwarfksyms supports writing a symtypes file for each processed object that contain types for exported @@ -85,8 +85,8 @@ produces C-style type strings, gendwarfksyms uses the same simple parsed DWARF format produced by **--dump-dies**, but with type references instead of fully expanded strings. -4. Maintaining a stable kABI -============================ +Maintaining a stable kABI +========================= Distribution maintainers often need the ability to make ABI compatible changes to kernel data structures due to LTS updates or backports. Using @@ -104,8 +104,8 @@ for source code annotation. Note that as these features are only used to transform the inputs for symbol versioning, the user is responsible for ensuring that their changes actually won't break the ABI. -4.1. kABI rules -=============== +kABI rules +---------- kABI rules allow distributions to fine-tune certain parts of gendwarfksyms output and thus control how symbol @@ -139,8 +139,8 @@ Currently, only the rules discussed in this section are supported, but the format is extensible enough to allow further rules to be added as need arises. -4.1.1. Managing definition visibility -===================================== +Managing definition visibility +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A declaration can change into a full definition when additional includes are pulled into the translation unit. This changes the versions of any @@ -168,8 +168,8 @@ Example usage:: KABI_DECLONLY(s); -4.1.2. Adding enumerators -========================= +Adding enumerators +~~~~~~~~~~~~~~~~~~ For enums, all enumerators and their values are included in calculating symbol versions, which becomes a problem if we later need to add more @@ -223,8 +223,8 @@ Example usage:: KABI_ENUMERATOR_IGNORE(e, C); KABI_ENUMERATOR_VALUE(e, LAST, 2); -4.3. Adding structure members -============================= +Adding structure members +------------------------ Perhaps the most common ABI compatible change is adding a member to a kernel data structure. When changes to a structure are anticipated, @@ -237,8 +237,8 @@ natural method. This section describes gendwarfksyms support for using reserved space in data structures and hiding members that don't change the ABI when calculating symbol versions. -4.3.1. Reserving space and replacing members -============================================ +Reserving space and replacing members +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Space is typically reserved for later use by appending integer types, or arrays, to the end of the data structure, but any type can be used. Each @@ -276,8 +276,8 @@ The examples include `KABI_(RESERVE|USE|REPLACE)*` macros that help simplify the process and also ensure the replacement member is correctly aligned and its size won't exceed the reserved space. -4.3.2. Hiding members -===================== +Hiding members +~~~~~~~~~~~~~~ Predicting which structures will require changes during the support timeframe isn't always possible, in which case one might have to resort -- cgit v1.2.3-59-g8ed1b From cf9d692629fa3017989367ac86bf34109176cdad Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Wed, 7 May 2025 23:14:09 +0000 Subject: Documentation/kbuild: Add new gendwarfksyms kABI rules Document the "byte_size" and "type_string" kABI stability rules. Signed-off-by: Sami Tolvanen Signed-off-by: Masahiro Yamada --- Documentation/kbuild/gendwarfksyms.rst | 97 ++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 5 deletions(-) diff --git a/Documentation/kbuild/gendwarfksyms.rst b/Documentation/kbuild/gendwarfksyms.rst index 9694ec99d190..ed366250a54e 100644 --- a/Documentation/kbuild/gendwarfksyms.rst +++ b/Documentation/kbuild/gendwarfksyms.rst @@ -125,14 +125,17 @@ the rules. The fields are as follows: qualified name of the DWARF Debugging Information Entry (DIE). - `value`: Provides rule-specific data. -The following helper macro, for example, can be used to specify rules +The following helper macros, for example, can be used to specify rules in the source code:: - #define __KABI_RULE(hint, target, value) \ - static const char __PASTE(__gendwarfksyms_rule_, \ + #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) Currently, only the rules discussed in this section are supported, but @@ -223,6 +226,87 @@ Example usage:: KABI_ENUMERATOR_IGNORE(e, C); KABI_ENUMERATOR_VALUE(e, LAST, 2); +Managing structure size changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +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. + +To append new members, we can hide them from symbol versioning as +described in section :ref:`Hiding members `, but we can't +hide the increase in structure size. The `byte_size` rule allows us to +override the structure size used for symbol versioning. + +The rule fields are expected to be as follows: + +- `type`: "byte_size" +- `target`: The fully qualified name of the target data structure + (as shown in **--dump-dies** output). +- `value`: A positive decimal number indicating the structure size + in bytes. + +Using the `__KABI_RULE` macro, this rule can be defined as:: + + #define KABI_BYTE_SIZE(fqn, value) \ + __KABI_RULE(byte_size, fqn, value) + +Example usage:: + + struct s { + /* Unchanged original members */ + unsigned long a; + void *p; + + /* Appended new members */ + KABI_IGNORE(0, unsigned long n); + }; + + KABI_BYTE_SIZE(s, 16); + +Overriding 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 more +targeted kABI rules can become tedious. The `type_string` rule allows us +to override the full type string for a type or a symbol, and even add +types for versioning that no longer exist in the kernel. + +The rule fields are expected to be as follows: + +- `type`: "type_string" +- `target`: The fully qualified name of the target data structure + (as shown in **--dump-dies** output) or symbol. +- `value`: A valid type string (as shown in **--symtypes**) output) + to use instead of the real type. + +Using the `__KABI_RULE` macro, this rule can be defined as:: + + #define KABI_TYPE_STRING(type, str) \ + ___KABI_RULE("type_string", type, str) + +Example usage:: + + /* Override type for a structure */ + KABI_TYPE_STRING("s#s", + "structure_type s { " + "member base_type int byte_size(4) " + "encoding(5) n " + "data_member_location(0) " + "} byte_size(8)"); + + /* Override type for a symbol */ + KABI_TYPE_STRING("my_symbol", "variable s#s"); + +The `type_string` rule should be used only as a last resort if maintaining +a stable symbol versions cannot be reasonably achieved using other +means. Overriding a type string increases the risk of actual ABI breakages +going unnoticed as it hides all changes to the type. + Adding structure members ------------------------ @@ -276,6 +360,8 @@ The examples include `KABI_(RESERVE|USE|REPLACE)*` macros that help simplify the process and also ensure the replacement member is correctly aligned and its size won't exceed the reserved space. +.. _hiding_members: + Hiding members ~~~~~~~~~~~~~~ @@ -305,4 +391,5 @@ member to a union where one of the fields has a name starting with unsigned long b; }; -With **--stable**, both versions produce the same symbol version. +With **--stable**, both versions produce the same symbol version. The +examples include a `KABI_IGNORE` macro to simplify the code. -- cgit v1.2.3-59-g8ed1b From 2adde2eb1638336e17f887070e6dfc52205d464b Mon Sep 17 00:00:00 2001 From: Henrik Lindström Date: Mon, 12 May 2025 18:10:06 +0200 Subject: Makefile: remove dependency on archscripts for header installation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit archscripts has nothing to do with headers_install. Signed-off-by: Henrik Lindström Reviewed-by: Nicolas Schier Signed-off-by: Masahiro Yamada --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a9edd0303653..efbc0966b82a 100644 --- a/Makefile +++ b/Makefile @@ -1365,7 +1365,7 @@ PHONY += archheaders archscripts hdr-inst := -f $(srctree)/scripts/Makefile.headersinst obj PHONY += headers -headers: $(version_h) scripts_unifdef uapi-asm-generic archheaders archscripts +headers: $(version_h) scripts_unifdef uapi-asm-generic archheaders ifdef HEADER_ARCH $(Q)$(MAKE) -f $(srctree)/Makefile HEADER_ARCH= SRCARCH=$(HEADER_ARCH) headers else -- cgit v1.2.3-59-g8ed1b From 8f81d8529e19c84b014e4239d2012373274d138d Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 14 May 2025 14:46:33 +0900 Subject: kbuild: move kbuild syntax processing to scripts/Makefile.build scripts/Makefile.lib is included by the following Makefiles: scripts/Makefile.build scripts/Makefile.modfinal scripts/Makefile.package scripts/Makefile.vmlinux scripts/Makefile.vmlinux_o However, the last four do not need to process Kbuild syntax such as obj-*, lib-*, subdir-*, etc. Move the relevant code to scripts/Makefile.build. Signed-off-by: Masahiro Yamada Reviewed-by: Nicolas Schier --- scripts/Makefile.build | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++ scripts/Makefile.lib | 84 -------------------------------------------------- 2 files changed, 84 insertions(+), 84 deletions(-) diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 13dcd86e74ca..8d8252229895 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -37,6 +37,90 @@ include $(srctree)/scripts/Makefile.compiler include $(kbuild-file) include $(srctree)/scripts/Makefile.lib +# flags that take effect in current and sub directories +KBUILD_AFLAGS += $(subdir-asflags-y) +KBUILD_CFLAGS += $(subdir-ccflags-y) +KBUILD_RUSTFLAGS += $(subdir-rustflags-y) + +# Figure out what we need to build from the various variables +# =========================================================================== + +# When an object is listed to be built compiled-in and modular, +# only build the compiled-in version +obj-m := $(filter-out $(obj-y),$(obj-m)) + +# Libraries are always collected in one lib file. +# Filter out objects already built-in +lib-y := $(filter-out $(obj-y), $(sort $(lib-y) $(lib-m))) + +# Subdirectories we need to descend into +subdir-ym := $(sort $(subdir-y) $(subdir-m) \ + $(patsubst %/,%, $(filter %/, $(obj-y) $(obj-m)))) + +# Handle objects in subdirs: +# - If we encounter foo/ in $(obj-y), replace it by foo/built-in.a and +# foo/modules.order +# - If we encounter foo/ in $(obj-m), replace it by foo/modules.order +# +# Generate modules.order to determine modorder. Unfortunately, we don't have +# information about ordering between -y and -m subdirs. Just put -y's first. + +ifdef need-modorder +obj-m := $(patsubst %/,%/modules.order, $(filter %/, $(obj-y)) $(obj-m)) +else +obj-m := $(filter-out %/, $(obj-m)) +endif + +ifdef need-builtin +obj-y := $(patsubst %/, %/built-in.a, $(obj-y)) +else +obj-y := $(filter-out %/, $(obj-y)) +endif + +# Expand $(foo-objs) $(foo-y) etc. by replacing their individuals +suffix-search = $(strip $(foreach s, $3, $($(1:%$(strip $2)=%$s)))) +# List composite targets that are constructed by combining other targets +multi-search = $(sort $(foreach m, $1, $(if $(call suffix-search, $m, $2, $3 -), $m))) +# List primitive targets that are compiled from source files +real-search = $(foreach m, $1, $(if $(call suffix-search, $m, $2, $3 -), $(call suffix-search, $m, $2, $3), $m)) + +# If $(foo-objs), $(foo-y), $(foo-m), or $(foo-) exists, foo.o is a composite object +multi-obj-y := $(call multi-search, $(obj-y), .o, -objs -y) +multi-obj-m := $(call multi-search, $(obj-m), .o, -objs -y -m) +multi-obj-ym := $(multi-obj-y) $(multi-obj-m) + +# Replace multi-part objects by their individual parts, +# including built-in.a from subdirectories +real-obj-y := $(call real-search, $(obj-y), .o, -objs -y) +real-obj-m := $(call real-search, $(obj-m), .o, -objs -y -m) + +always-y += $(always-m) + +# hostprogs-always-y += foo +# ... is a shorthand for +# hostprogs += foo +# always-y += foo +hostprogs += $(hostprogs-always-y) $(hostprogs-always-m) +always-y += $(hostprogs-always-y) $(hostprogs-always-m) + +# userprogs-always-y is likewise. +userprogs += $(userprogs-always-y) $(userprogs-always-m) +always-y += $(userprogs-always-y) $(userprogs-always-m) + +# Add subdir path + +ifneq ($(obj),.) +extra-y := $(addprefix $(obj)/, $(extra-y)) +always-y := $(addprefix $(obj)/, $(always-y)) +targets := $(addprefix $(obj)/, $(targets)) +obj-m := $(addprefix $(obj)/, $(obj-m)) +lib-y := $(addprefix $(obj)/, $(lib-y)) +real-obj-y := $(addprefix $(obj)/, $(real-obj-y)) +real-obj-m := $(addprefix $(obj)/, $(real-obj-m)) +multi-obj-m := $(addprefix $(obj)/, $(multi-obj-m)) +subdir-ym := $(addprefix $(obj)/, $(subdir-ym)) +endif + ifndef obj $(warning kbuild: Makefile.build is included improperly) endif diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 2fe73cda0bdd..2d3a8470cf39 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -1,89 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -# flags that take effect in current and sub directories -KBUILD_AFLAGS += $(subdir-asflags-y) -KBUILD_CFLAGS += $(subdir-ccflags-y) -KBUILD_RUSTFLAGS += $(subdir-rustflags-y) - -# Figure out what we need to build from the various variables -# =========================================================================== - -# When an object is listed to be built compiled-in and modular, -# only build the compiled-in version -obj-m := $(filter-out $(obj-y),$(obj-m)) - -# Libraries are always collected in one lib file. -# Filter out objects already built-in -lib-y := $(filter-out $(obj-y), $(sort $(lib-y) $(lib-m))) - -# Subdirectories we need to descend into -subdir-ym := $(sort $(subdir-y) $(subdir-m) \ - $(patsubst %/,%, $(filter %/, $(obj-y) $(obj-m)))) - -# Handle objects in subdirs: -# - If we encounter foo/ in $(obj-y), replace it by foo/built-in.a and -# foo/modules.order -# - If we encounter foo/ in $(obj-m), replace it by foo/modules.order -# -# Generate modules.order to determine modorder. Unfortunately, we don't have -# information about ordering between -y and -m subdirs. Just put -y's first. - -ifdef need-modorder -obj-m := $(patsubst %/,%/modules.order, $(filter %/, $(obj-y)) $(obj-m)) -else -obj-m := $(filter-out %/, $(obj-m)) -endif - -ifdef need-builtin -obj-y := $(patsubst %/, %/built-in.a, $(obj-y)) -else -obj-y := $(filter-out %/, $(obj-y)) -endif - -# Expand $(foo-objs) $(foo-y) etc. by replacing their individuals -suffix-search = $(strip $(foreach s, $3, $($(1:%$(strip $2)=%$s)))) -# List composite targets that are constructed by combining other targets -multi-search = $(sort $(foreach m, $1, $(if $(call suffix-search, $m, $2, $3 -), $m))) -# List primitive targets that are compiled from source files -real-search = $(foreach m, $1, $(if $(call suffix-search, $m, $2, $3 -), $(call suffix-search, $m, $2, $3), $m)) - -# If $(foo-objs), $(foo-y), $(foo-m), or $(foo-) exists, foo.o is a composite object -multi-obj-y := $(call multi-search, $(obj-y), .o, -objs -y) -multi-obj-m := $(call multi-search, $(obj-m), .o, -objs -y -m) -multi-obj-ym := $(multi-obj-y) $(multi-obj-m) - -# Replace multi-part objects by their individual parts, -# including built-in.a from subdirectories -real-obj-y := $(call real-search, $(obj-y), .o, -objs -y) -real-obj-m := $(call real-search, $(obj-m), .o, -objs -y -m) - -always-y += $(always-m) - -# hostprogs-always-y += foo -# ... is a shorthand for -# hostprogs += foo -# always-y += foo -hostprogs += $(hostprogs-always-y) $(hostprogs-always-m) -always-y += $(hostprogs-always-y) $(hostprogs-always-m) - -# userprogs-always-y is likewise. -userprogs += $(userprogs-always-y) $(userprogs-always-m) -always-y += $(userprogs-always-y) $(userprogs-always-m) - -# Add subdir path - -ifneq ($(obj),.) -extra-y := $(addprefix $(obj)/,$(extra-y)) -always-y := $(addprefix $(obj)/,$(always-y)) -targets := $(addprefix $(obj)/,$(targets)) -obj-m := $(addprefix $(obj)/,$(obj-m)) -lib-y := $(addprefix $(obj)/,$(lib-y)) -real-obj-y := $(addprefix $(obj)/,$(real-obj-y)) -real-obj-m := $(addprefix $(obj)/,$(real-obj-m)) -multi-obj-m := $(addprefix $(obj)/, $(multi-obj-m)) -subdir-ym := $(addprefix $(obj)/,$(subdir-ym)) -endif - # Finds the multi-part object the current object will be linked into. # If the object belongs to two or more multi-part objects, list them all. modname-multi = $(sort $(foreach m,$(multi-obj-ym),\ -- cgit v1.2.3-59-g8ed1b From 9c036cfbb75bb58ecce589f547fdb8af153493a6 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 22 May 2025 16:17:20 +0900 Subject: modpost: check forbidden MODULE_IMPORT_NS("module:") at compile time Explicitly adding MODULE_IMPORT_NS("module:...") is not allowed. Currently, this is only checked at run time. That is, when such a module is loaded, an error message like the following is shown: foo: module tries to import module namespace: module:bar Obviously, checking this at compile time improves usability. In such a case, modpost will report the following error at compile time: ERROR: modpost: foo: explicitly importing namespace "module:bar" is not allowed. Signed-off-by: Masahiro Yamada --- scripts/mod/modpost.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 16a69a129805..5ca7c268294e 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -28,6 +28,8 @@ #include "modpost.h" #include "../../include/linux/license.h" +#define MODULE_NS_PREFIX "module:" + static bool module_enabled; /* Are we using CONFIG_MODVERSIONS? */ static bool modversions; @@ -1597,8 +1599,13 @@ static void read_symbols(const char *modname) for (namespace = get_modinfo(&info, "import_ns"); namespace; - namespace = get_next_modinfo(&info, "import_ns", namespace)) + namespace = get_next_modinfo(&info, "import_ns", namespace)) { + if (strstarts(namespace, MODULE_NS_PREFIX)) + error("%s: explicitly importing namespace \"%s\" is not allowed.\n", + mod->name, namespace); + add_namespace(&mod->imported_namespaces, namespace); + } if (!get_modinfo(&info, "description")) warn("missing MODULE_DESCRIPTION() in %s\n", modname); -- cgit v1.2.3-59-g8ed1b From 40617439d572645207c1866dfb086de0be438a14 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 22 May 2025 16:17:22 +0900 Subject: docs/core-api/symbol-namespaces: drop table of contents and section numbering The manually updated table of contents and section numbering are hard to maintain. Make changes similar to the following commits: 5e8f0ba38a4d ("docs/kbuild/makefiles: throw out the local table of contents") 1a4c1c9df72e ("docs/kbuild/makefiles: drop section numbering, use references") Signed-off-by: Masahiro Yamada --- Documentation/core-api/symbol-namespaces.rst | 45 ++++++++-------------- .../it_IT/core-api/symbol-namespaces.rst | 32 ++++++++------- .../zh_CN/core-api/symbol-namespaces.rst | 41 ++++++++------------ 3 files changed, 47 insertions(+), 71 deletions(-) diff --git a/Documentation/core-api/symbol-namespaces.rst b/Documentation/core-api/symbol-namespaces.rst index c6f59c5e2564..f7cfa7b73e97 100644 --- a/Documentation/core-api/symbol-namespaces.rst +++ b/Documentation/core-api/symbol-namespaces.rst @@ -6,18 +6,8 @@ The following document describes how to use Symbol Namespaces to structure the export surface of in-kernel symbols exported through the family of EXPORT_SYMBOL() macros. -.. Table of Contents - - === 1 Introduction - === 2 How to define Symbol Namespaces - --- 2.1 Using the EXPORT_SYMBOL macros - --- 2.2 Using the DEFAULT_SYMBOL_NAMESPACE define - === 3 How to use Symbols exported in Namespaces - === 4 Loading Modules that use namespaced Symbols - === 5 Automatically creating MODULE_IMPORT_NS statements - -1. Introduction -=============== +Introduction +============ Symbol Namespaces have been introduced as a means to structure the export surface of the in-kernel API. It allows subsystem maintainers to partition @@ -31,15 +21,15 @@ its configuration, reject loading the module or warn about a missing import. Additionally, it is possible to put symbols into a module namespace, strictly limiting which modules are allowed to use these symbols. -2. How to define Symbol Namespaces -================================== +How to define Symbol Namespaces +=============================== Symbols can be exported into namespace using different methods. All of them are changing the way EXPORT_SYMBOL and friends are instrumented to create ksymtab entries. -2.1 Using the EXPORT_SYMBOL macros -================================== +Using the EXPORT_SYMBOL macros +------------------------------ In addition to the macros EXPORT_SYMBOL() and EXPORT_SYMBOL_GPL(), that allow exporting of kernel symbols to the kernel symbol table, variants of these are @@ -57,8 +47,8 @@ refer to ``NULL``. There is no default namespace if none is defined. ``modpost`` and kernel/module/main.c make use the namespace at build time or module load time, respectively. -2.2 Using the DEFAULT_SYMBOL_NAMESPACE define -============================================= +Using the DEFAULT_SYMBOL_NAMESPACE define +----------------------------------------- Defining namespaces for all symbols of a subsystem can be very verbose and may become hard to maintain. Therefore a default define (DEFAULT_SYMBOL_NAMESPACE) @@ -86,8 +76,8 @@ unit as preprocessor statement. The above example would then read:: within the corresponding compilation unit before the #include for . Typically it's placed before the first #include statement. -2.3 Using the EXPORT_SYMBOL_GPL_FOR_MODULES() macro -=================================================== +Using the EXPORT_SYMBOL_GPL_FOR_MODULES() macro +----------------------------------------------- Symbols exported using this macro are put into a module namespace. This namespace cannot be imported. @@ -102,8 +92,8 @@ For example: will limit usage of this symbol to modules whoes name matches the given patterns. -3. How to use Symbols exported in Namespaces -============================================ +How to use Symbols exported in Namespaces +========================================= In order to use symbols that are exported into namespaces, kernel modules need to explicitly import these namespaces. Otherwise the kernel might reject to @@ -125,11 +115,10 @@ inspected with modinfo:: It is advisable to add the MODULE_IMPORT_NS() statement close to other module -metadata definitions like MODULE_AUTHOR() or MODULE_LICENSE(). Refer to section -5. for a way to create missing import statements automatically. +metadata definitions like MODULE_AUTHOR() or MODULE_LICENSE(). -4. Loading Modules that use namespaced Symbols -============================================== +Loading Modules that use namespaced Symbols +=========================================== At module loading time (e.g. ``insmod``), the kernel will check each symbol referenced from the module for its availability and whether the namespace it @@ -140,8 +129,8 @@ allow loading of modules that don't satisfy this precondition, a configuration option is available: Setting MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS=y will enable loading regardless, but will emit a warning. -5. Automatically creating MODULE_IMPORT_NS statements -===================================================== +Automatically creating MODULE_IMPORT_NS statements +================================================== Missing namespaces imports can easily be detected at build time. In fact, modpost will emit a warning if a module uses a symbol from a namespace diff --git a/Documentation/translations/it_IT/core-api/symbol-namespaces.rst b/Documentation/translations/it_IT/core-api/symbol-namespaces.rst index 6ee713988531..baa344f4523a 100644 --- a/Documentation/translations/it_IT/core-api/symbol-namespaces.rst +++ b/Documentation/translations/it_IT/core-api/symbol-namespaces.rst @@ -10,8 +10,8 @@ Questo documento descrive come usare lo spazio dei nomi dei simboli per strutturare quello che viene esportato internamente al kernel grazie alle macro della famiglia EXPORT_SYMBOL(). -1. Introduzione -=============== +Introduzione +============ Lo spazio dei nomi dei simboli è stato introdotto come mezzo per strutturare l'API esposta internamente al kernel. Permette ai manutentori di un @@ -24,15 +24,15 @@ devono prima importare detto spazio. Altrimenti il kernel, a seconda della configurazione, potrebbe rifiutare di caricare il modulo o avvisare l'utente di un'importazione mancante. -2. Come definire uno spazio dei nomi dei simboli -================================================ +Come definire uno spazio dei nomi dei simboli +============================================= I simboli possono essere esportati in spazi dei nomi usando diversi meccanismi. Tutti questi meccanismi cambiano il modo in cui EXPORT_SYMBOL e simili vengono guidati verso la creazione di voci in ksymtab. -2.1 Usare le macro EXPORT_SYMBOL -================================ +Usare le macro EXPORT_SYMBOL +---------------------------- In aggiunta alle macro EXPORT_SYMBOL() e EXPORT_SYMBOL_GPL(), che permettono di esportare simboli del kernel nella rispettiva tabella, ci sono @@ -53,8 +53,8 @@ di base. Il programma ``modpost`` e il codice in kernel/module/main.c usano lo spazio dei nomi, rispettivamente, durante la compilazione e durante il caricamento di un modulo. -2.2 Usare il simbolo di preprocessore DEFAULT_SYMBOL_NAMESPACE -============================================================== +Usare il simbolo di preprocessore DEFAULT_SYMBOL_NAMESPACE +---------------------------------------------------------- Definire lo spazio dei nomi per tutti i simboli di un sottosistema può essere logorante e di difficile manutenzione. Perciò è stato fornito un simbolo @@ -83,8 +83,8 @@ direttamente nei file da compilare. L'esempio precedente diventerebbe:: Questo va messo prima di un qualsiasi uso di EXPORT_SYMBOL. -3. Come usare i simboli esportati attraverso uno spazio dei nomi -================================================================ +Come usare i simboli esportati attraverso uno spazio dei nomi +============================================================= Per usare i simboli esportati da uno spazio dei nomi, i moduli del kernel devono esplicitamente importare il relativo spazio dei nomi; altrimenti @@ -108,12 +108,10 @@ modinfo:: Si consiglia di posizionare la dichiarazione MODULE_IMPORT_NS() vicino -ai metadati del modulo come MODULE_AUTHOR() o MODULE_LICENSE(). Fate -riferimento alla sezione 5. per creare automaticamente le importazioni -mancanti. +ai metadati del modulo come MODULE_AUTHOR() o MODULE_LICENSE(). -4. Caricare moduli che usano simboli provenienti da spazi dei nomi -================================================================== +Caricare moduli che usano simboli provenienti da spazi dei nomi +=============================================================== Quando un modulo viene caricato (per esempio usando ``insmod``), il kernel verificherà la disponibilità di ogni simbolo usato e se lo spazio dei nomi @@ -125,8 +123,8 @@ un'opzione di configurazione: impostare MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS=y caricherà i moduli comunque ma emetterà un avviso. -5. Creare automaticamente la dichiarazione MODULE_IMPORT_NS -=========================================================== +Creare automaticamente la dichiarazione MODULE_IMPORT_NS +======================================================== La mancanza di un'importazione può essere individuata facilmente al momento della compilazione. Infatti, modpost emetterà un avviso se il modulo usa diff --git a/Documentation/translations/zh_CN/core-api/symbol-namespaces.rst b/Documentation/translations/zh_CN/core-api/symbol-namespaces.rst index b1bec219912d..d9477ccea98f 100644 --- a/Documentation/translations/zh_CN/core-api/symbol-namespaces.rst +++ b/Documentation/translations/zh_CN/core-api/symbol-namespaces.rst @@ -14,18 +14,8 @@ 本文档描述了如何使用符号命名空间来构造通过EXPORT_SYMBOL()系列宏导出的内核内符号的导出面。 -.. 目录 - - === 1 简介 - === 2 如何定义符号命名空间 - --- 2.1 使用EXPORT_SYMBOL宏 - --- 2.2 使用DEFAULT_SYMBOL_NAMESPACE定义 - === 3 如何使用命名空间中导出的符号 - === 4 加载使用命名空间符号的模块 - === 5 自动创建MODULE_IMPORT_NS声明 - -1. 简介 -======= +简介 +==== 符号命名空间已经被引入,作为构造内核内API的导出面的一种手段。它允许子系统维护者将 他们导出的符号划分进独立的命名空间。这对于文档的编写非常有用(想想SUBSYSTEM_DEBUG @@ -33,14 +23,14 @@ 的模块必须导入命名空间。否则,内核将根据其配置,拒绝加载该模块或警告说缺少 导入。 -2. 如何定义符号命名空间 -======================= +如何定义符号命名空间 +==================== 符号可以用不同的方法导出到命名空间。所有这些都在改变 EXPORT_SYMBOL 和与之类似的那些宏 被检测到的方式,以创建 ksymtab 条目。 -2.1 使用EXPORT_SYMBOL宏 -======================= +使用EXPORT_SYMBOL宏 +------------------- 除了允许将内核符号导出到内核符号表的宏EXPORT_SYMBOL()和EXPORT_SYMBOL_GPL()之外, 这些宏的变体还可以将符号导出到某个命名空间:EXPORT_SYMBOL_NS() 和 EXPORT_SYMBOL_NS_GPL()。 @@ -54,8 +44,8 @@ 导出时未指明命名空间的符号将指向 ``NULL`` 。如果没有定义命名空间,则默认没有。 ``modpost`` 和kernel/module/main.c分别在构建时或模块加载时使用名称空间。 -2.2 使用DEFAULT_SYMBOL_NAMESPACE定义 -==================================== +使用DEFAULT_SYMBOL_NAMESPACE定义 +-------------------------------- 为一个子系统的所有符号定义命名空间可能会非常冗长,并可能变得难以维护。因此,我 们提供了一个默认定义(DEFAULT_SYMBOL_NAMESPACE),如果设置了这个定义, 它将成 @@ -80,8 +70,8 @@ 应置于相关编译单元中任何 EXPORT_SYMBOL 宏之前 -3. 如何使用命名空间中导出的符号 -=============================== +如何使用命名空间中导出的符号 +============================ 为了使用被导出到命名空间的符号,内核模块需要明确地导入这些命名空间。 否则内核可能会拒绝加载该模块。模块代码需要使用宏MODULE_IMPORT_NS来 @@ -100,11 +90,10 @@ 建议将 MODULE_IMPORT_NS() 语句添加到靠近其他模块元数据定义的地方, -如 MODULE_AUTHOR() 或 MODULE_LICENSE() 。关于自动创建缺失的导入 -语句的方法,请参考第5节。 +如 MODULE_AUTHOR() 或 MODULE_LICENSE() 。 -4. 加载使用命名空间符号的模块 -============================= +加载使用命名空间符号的模块 +========================== 在模块加载时(比如 ``insmod`` ),内核将检查每个从模块中引用的符号是否可 用,以及它可能被导出到的名字空间是否被模块导入。内核的默认行为是拒绝 @@ -113,8 +102,8 @@ EINVAL方式失败。要允许加载不满足这个前提条件的模块,可 设置 MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS=y 将使加载不受影响,但会 发出警告。 -5. 自动创建MODULE_IMPORT_NS声明 -=============================== +自动创建MODULE_IMPORT_NS声明 +============================ 缺少命名空间的导入可以在构建时很容易被检测到。事实上,如果一个模块 使用了一个命名空间的符号而没有导入它,modpost会发出警告。 -- cgit v1.2.3-59-g8ed1b From 81a93bf93f0e5963d8695a3596ac50b5ceea87c3 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 26 May 2025 18:07:51 +0900 Subject: tinyconfig: enable CONFIG_LD_DEAD_CODE_DATA_ELIMINATION This CONFIG option, if supported by the architecture, helps reduce the size of vmlinux. For example, the size of vmlinux with ARCH=arm tinyconfig decreases as follows: text data bss dec hex filename 631684 104500 18176 754360 b82b8 vmlinux.before 455316 93404 15472 564192 89be0 vmlinux.after Signed-off-by: Masahiro Yamada --- kernel/configs/tiny.config | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/configs/tiny.config b/kernel/configs/tiny.config index b753695c5a8f..5dd0f0a34a73 100644 --- a/kernel/configs/tiny.config +++ b/kernel/configs/tiny.config @@ -2,3 +2,4 @@ CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_KERNEL_XZ=y CONFIG_SLUB=y CONFIG_SLUB_TINY=y +CONFIG_LD_DEAD_CODE_DATA_ELIMINATION=y -- cgit v1.2.3-59-g8ed1b From 41a77d43f36d3b897755a223b264c45131546b8b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 26 May 2025 18:07:52 +0900 Subject: kbuild: link lib-y objects to vmlinux forcibly even when CONFIG_MODULES=n Since commit 7273ad2b08f8 ("kbuild: link lib-y objects to vmlinux forcibly when CONFIG_MODULES=y"), all objects from lib-y have been forcibly linked to vmlinux when CONFIG_MODULES=y. To simplify future changes, this commit makes all objects from lib-y be linked regardless of the CONFIG_MODULES setting. Most use cases (CONFIG_MODULES=y) are not affected by this change. The vmlinux size with ARCH=arm allnoconfig, where CONFIG_MODULES=n, increases as follows: text data bss dec hex filename 1368644 835104 206288 2410036 24c634 vmlinux.before 1379440 837064 206288 2422792 24f808 vmlinux.after We no longer benefit from using static libraries, but the impact is mitigated by supporting CONFIG_LD_DEAD_CODE_DATA_ELIMINATION. For example, the size of vmlinux remains almost the same with ARCH=arm tinyconfig, where CONFIG_MODULES=n and CONFIG_LD_DEAD_CODE_DATA_ELIMINATION=y. text data bss dec hex filename 455316 93404 15472 564192 89be0 vmlinux.before 455312 93404 15472 564188 89bdc vmlinux.after Signed-off-by: Masahiro Yamada --- Makefile | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Makefile b/Makefile index efbc0966b82a..9a104f02ce25 100644 --- a/Makefile +++ b/Makefile @@ -1184,13 +1184,8 @@ export ARCH_LIB := $(filter %/, $(libs-y)) export ARCH_DRIVERS := $(drivers-y) $(drivers-m) # Externally visible symbols (used by link-vmlinux.sh) -KBUILD_VMLINUX_OBJS := ./built-in.a -ifdef CONFIG_MODULES -KBUILD_VMLINUX_OBJS += $(patsubst %/, %/lib.a, $(filter %/, $(libs-y))) +KBUILD_VMLINUX_OBJS := built-in.a $(patsubst %/, %/lib.a, $(filter %/, $(libs-y))) KBUILD_VMLINUX_LIBS := $(filter-out %/, $(libs-y)) -else -KBUILD_VMLINUX_LIBS := $(patsubst %/,%/lib.a, $(libs-y)) -endif export KBUILD_VMLINUX_LIBS export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds -- cgit v1.2.3-59-g8ed1b From 94145ffb07601674091aadbdca9ce004b1736df4 Mon Sep 17 00:00:00 2001 From: Khaled Elnaggar Date: Tue, 27 May 2025 00:10:39 +0300 Subject: docs: symbol-namespaces: fix reST warning with literal block Use a literal block for the EXPORT_SYMBOL_GPL_FOR_MODULES() example to avoid a Docutils warning about unmatched '*'. This ensures correct rendering and keeps the source readable. Warning: Documentation/core-api/symbol-namespaces.rst:90: WARNING: Inline emphasis start-string without end-string. [docutils] Signed-off-by: Khaled Elnaggar Reviewed-by: Bagas Sanjaya Signed-off-by: Masahiro Yamada --- Documentation/core-api/symbol-namespaces.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/core-api/symbol-namespaces.rst b/Documentation/core-api/symbol-namespaces.rst index f7cfa7b73e97..32fc73dc5529 100644 --- a/Documentation/core-api/symbol-namespaces.rst +++ b/Documentation/core-api/symbol-namespaces.rst @@ -85,7 +85,7 @@ namespace cannot be imported. The macro takes a comma separated list of module names, allowing only those modules to access this symbol. Simple tail-globs are supported. -For example: +For example:: EXPORT_SYMBOL_GPL_FOR_MODULES(preempt_notifier_inc, "kvm,kvm-*") -- cgit v1.2.3-59-g8ed1b From 0f57c75973bedc08a865b06ce1b73ae5b54477c0 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 28 May 2025 02:56:15 +0900 Subject: kconfig: introduce menu type enum Currently, menu->prompt->type is checked to distinguish "comment" (P_COMMENT) and "menu" (P_MENU) entries from regular "config" entries. This is odd because P_COMMENT and P_MENU are not properties. This commit introduces menu type enum to distinguish menu types more naturally. Signed-off-by: Masahiro Yamada --- scripts/kconfig/expr.h | 11 +++++++++++ scripts/kconfig/lkc.h | 2 +- scripts/kconfig/menu.c | 5 +++-- scripts/kconfig/parser.y | 12 ++++++------ 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index 21578dcd4292..fe2231e0e6a4 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -205,15 +205,26 @@ struct property { for (st = sym->prop; st; st = st->next) \ if (st->text) +enum menu_type { + M_CHOICE, // "choice" + M_COMMENT, // "comment" + M_IF, // "if" + M_MENU, // "mainmenu", "menu", "menuconfig" + M_NORMAL, // others, i.e., "config" +}; + /* * Represents a node in the menu tree, as seen in e.g. menuconfig (though used * for all front ends). Each symbol, menu, etc. defined in the Kconfig files * gets a node. A symbol defined in multiple locations gets one node at each * location. * + * @type: type of the menu entry * @choice_members: list of choice members with priority. */ struct menu { + enum menu_type type; + /* The next menu node at the same level */ struct menu *next; diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index b8ebc3094a23..fbc907f75eac 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -81,7 +81,7 @@ void _menu_init(void); void menu_warn(const struct menu *menu, const char *fmt, ...); struct menu *menu_add_menu(void); void menu_end_menu(void); -void menu_add_entry(struct symbol *sym); +void menu_add_entry(struct symbol *sym, enum menu_type type); void menu_add_dep(struct expr *dep); void menu_add_visibility(struct expr *dep); struct property *menu_add_prompt(enum prop_type type, const char *prompt, diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index 6587ac86d0d5..7d48a692bd27 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -15,7 +15,7 @@ static const char nohelp_text[] = "There is no help available for this option."; -struct menu rootmenu; +struct menu rootmenu = { .type = M_MENU }; static struct menu **last_entry_ptr; /** @@ -65,12 +65,13 @@ void _menu_init(void) last_entry_ptr = &rootmenu.list; } -void menu_add_entry(struct symbol *sym) +void menu_add_entry(struct symbol *sym, enum menu_type type) { struct menu *menu; menu = xmalloc(sizeof(*menu)); memset(menu, 0, sizeof(*menu)); + menu->type = type; menu->sym = sym; menu->parent = current_menu; menu->filename = cur_filename; diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y index 68372d3ff325..e9c3c664e925 100644 --- a/scripts/kconfig/parser.y +++ b/scripts/kconfig/parser.y @@ -139,7 +139,7 @@ stmt_list_in_choice: config_entry_start: T_CONFIG nonconst_symbol T_EOL { - menu_add_entry($2); + menu_add_entry($2, M_NORMAL); printd(DEBUG_PARSE, "%s:%d:config %s\n", cur_filename, cur_lineno, $2->name); }; @@ -173,7 +173,7 @@ config_stmt: config_entry_start config_option_list menuconfig_entry_start: T_MENUCONFIG nonconst_symbol T_EOL { - menu_add_entry($2); + menu_add_entry($2, M_MENU); printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", cur_filename, cur_lineno, $2->name); }; @@ -246,7 +246,7 @@ choice: T_CHOICE T_EOL { struct symbol *sym = sym_lookup(NULL, 0); - menu_add_entry(sym); + menu_add_entry(sym, M_CHOICE); menu_set_type(S_BOOLEAN); INIT_LIST_HEAD(¤t_entry->choice_members); @@ -315,7 +315,7 @@ default: if_entry: T_IF expr T_EOL { printd(DEBUG_PARSE, "%s:%d:if\n", cur_filename, cur_lineno); - menu_add_entry(NULL); + menu_add_entry(NULL, M_IF); menu_add_dep($2); $$ = menu_add_menu(); }; @@ -338,7 +338,7 @@ if_stmt_in_choice: if_entry stmt_list_in_choice if_end menu: T_MENU T_WORD_QUOTE T_EOL { - menu_add_entry(NULL); + menu_add_entry(NULL, M_MENU); menu_add_prompt(P_MENU, $2, NULL); printd(DEBUG_PARSE, "%s:%d:menu\n", cur_filename, cur_lineno); }; @@ -376,7 +376,7 @@ source_stmt: T_SOURCE T_WORD_QUOTE T_EOL comment: T_COMMENT T_WORD_QUOTE T_EOL { - menu_add_entry(NULL); + menu_add_entry(NULL, M_COMMENT); menu_add_prompt(P_COMMENT, $2, NULL); printd(DEBUG_PARSE, "%s:%d:comment\n", cur_filename, cur_lineno); }; -- cgit v1.2.3-59-g8ed1b From a503a313108e26402151f25c1f2628ec91bda605 Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Fri, 30 May 2025 04:46:33 +0900 Subject: scripts/tags.sh: allow to use alternative ctags implementation Some ctags implementations are available. With this change, You can specify your favorite one with CTAGS environment variable. Signed-off-by: Masatake YAMATO Signed-off-by: Masahiro Yamada --- scripts/tags.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tags.sh b/scripts/tags.sh index 98680e9cd7be..99ce427d9a69 100755 --- a/scripts/tags.sh +++ b/scripts/tags.sh @@ -344,7 +344,7 @@ case "$1" in "tags") rm -f tags - xtags ctags + xtags ${CTAGS:-ctags} remove_structs=y ;; -- cgit v1.2.3-59-g8ed1b From 89e7fecf5ce2e85a323e58f09aa808218a37079a Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 Jun 2025 22:31:27 +0900 Subject: kbuild: move W=1 check for scripts/misc-check to top-level Makefile This script is executed only when ${KBUILD_EXTRA_WARN} contains 1. Move this check to the top-level Makefile to allow more checks to be easily added to this script. Signed-off-by: Masahiro Yamada Reviewed-by: Nathan Chancellor --- Makefile | 3 +++ scripts/misc-check | 9 +-------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 9a104f02ce25..fe2f1cb4d9b7 100644 --- a/Makefile +++ b/Makefile @@ -1827,9 +1827,12 @@ rustfmtcheck: rustfmt # Misc # --------------------------------------------------------------------------- +# Run misc checks when ${KBUILD_EXTRA_WARN} contains 1 PHONY += misc-check +ifneq ($(findstring 1,$(KBUILD_EXTRA_WARN)),) misc-check: $(Q)$(srctree)/scripts/misc-check +endif all: misc-check diff --git a/scripts/misc-check b/scripts/misc-check index d40d5484e0c5..f37b2f6931cc 100755 --- a/scripts/misc-check +++ b/scripts/misc-check @@ -3,15 +3,8 @@ set -e -# Detect files that are tracked but ignored by git. This is checked only when -# ${KBUILD_EXTRA_WARN} contains 1, git is installed, and the source tree is -# tracked by git. +# Detect files that are tracked but ignored by git. check_tracked_ignored_files () { - case "${KBUILD_EXTRA_WARN}" in - *1*) ;; - *) return;; - esac - git -C ${srctree:-.} ls-files -i -c --exclude-per-directory=.gitignore 2>/dev/null | sed 's/$/: warning: ignored by one of the .gitignore files/' >&2 } -- cgit v1.2.3-59-g8ed1b From 3a44052b728e5d96ea425f908e71926364a12f11 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 Jun 2025 22:31:28 +0900 Subject: scripts/misc-check: add double-quotes to satisfy shellcheck In scripts/misc-check line 8: git -C ${srctree:-.} ls-files -i -c --exclude-per-directory=.gitignore 2>/dev/null | ^-----------^ SC2086 (info): Double quote to prevent globbing and word splitting. Signed-off-by: Masahiro Yamada --- scripts/misc-check | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/misc-check b/scripts/misc-check index f37b2f6931cc..21551d721079 100755 --- a/scripts/misc-check +++ b/scripts/misc-check @@ -5,7 +5,7 @@ set -e # Detect files that are tracked but ignored by git. check_tracked_ignored_files () { - git -C ${srctree:-.} ls-files -i -c --exclude-per-directory=.gitignore 2>/dev/null | + git -C "${srctree:-.}" ls-files -i -c --exclude-per-directory=.gitignore 2>/dev/null | sed 's/$/: warning: ignored by one of the .gitignore files/' >&2 } -- cgit v1.2.3-59-g8ed1b From a934a57a42f64a40705202f84144b1a29b29f910 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 Jun 2025 22:31:29 +0900 Subject: scripts/misc-check: check missing #include when W=1 The problem was described in commit 5b20755b7780 ("init: move THIS_MODULE from to "). To summarize it again here: is included by most C files, even though only some of them actually export symbols. This is because some headers, such as include/linux/{module.h,linkage}, needlessly include . I have added a more detailed explanation in the comments of scripts/misc-check. This problem will be fixed in two steps: 1. Add #include directly to C files that use EXPORT_SYMBOL() 2. Remove #include from header files that do not use EXPORT_SYMBOL() This commit addresses step 1; scripts/misc-check will warn about *.[ch] files that use EXPORT_SYMBOL() but do not include . This check is only triggered when the kernel is built with W=1. We need to fix 4000+ files. I hope others will help with this effort. Signed-off-by: Masahiro Yamada --- scripts/misc-check | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/scripts/misc-check b/scripts/misc-check index 21551d721079..7cb61841a125 100755 --- a/scripts/misc-check +++ b/scripts/misc-check @@ -9,4 +9,47 @@ check_tracked_ignored_files () { sed 's/$/: warning: ignored by one of the .gitignore files/' >&2 } +# Check for missing #include +# +# The rule for including is very simple: +# Include only when you use EXPORT_SYMBOL(). That's it. +# +# However, some headers include even though they are completely +# unrelated to EXPORT_SYMBOL(). +# +# One example is include/linux/module.h. Please note and +# are orthogonal. should be included by files +# that can be compiled as modules. In other words, should be +# included by EXPORT_SYMBOL consumers. In contrast, should be +# included from EXPORT_SYMBOL providers, which may or may not be modular. +# Hence, include/linux/module.h should *not* include . +# +# Another example is include/linux/linkage.h, which is completely unrelated to +# EXPORT_SYMBOL(). Worse, it is included by most C files, which means, most C +# files end up including , even though only some of them +# actually export symbols. Hence, include/linux/linkage.h should *not* include +# . +# +# Before fixing such headers, we must ensure that C files using EXPORT_SYMBOL() +# include directly, since many C files currently rely on +# being included indirectly (likely, via etc.). +# +# Therefore, this check. +# +# The problem is simple - the warned files use EXPORT_SYMBOL(), but do not +# include . Please add #include to them. +# +# If the included headers are sorted alphabetically, please insert +# in the appropriate position to maintain the sort order. +# For this reason, this script only checks missing , but +# does not automatically fix it. +check_missing_include_linux_export_h () { + + git -C "${srctree:-.}" grep --files-with-matches -E 'EXPORT_SYMBOL((_NS)?(_GPL)?|_GPL_FOR_MODULES)\(.*\)' \ + -- '*.[ch]' :^tools/ :^include/linux/export.h | + xargs -r git -C "${srctree:-.}" grep --files-without-match '#include[[:space:]]*' | + xargs -r printf "%s: warning: EXPORT_SYMBOL() is used, but #include is missing\n" >&2 +} + check_tracked_ignored_files +check_missing_include_linux_export_h -- cgit v1.2.3-59-g8ed1b From 7d95680d64ac8e836c35fd56efe77eac4e9cc26b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 Jun 2025 22:31:30 +0900 Subject: scripts/misc-check: check unnecessary #include when W=1 Another issue with is that it is sometimes included even when EXPORT_SYMBOL() is not used at all. Some headers (e.g. include/linux/linkage.h>) cannot be fixed for now for the reason described in the previous commit. This commit adds a warning for *.c files that include but do not use EXPORT_SYMBOL() when the kernel is built with W=1. Signed-off-by: Masahiro Yamada --- scripts/misc-check | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/misc-check b/scripts/misc-check index 7cb61841a125..a74450e799d1 100755 --- a/scripts/misc-check +++ b/scripts/misc-check @@ -51,5 +51,17 @@ check_missing_include_linux_export_h () { xargs -r printf "%s: warning: EXPORT_SYMBOL() is used, but #include is missing\n" >&2 } +# If you do not use EXPORT_SYMBOL(), please do not include . +# Currently, this is checked for *.c files, but not for *.h files, because some +# *.c files rely on being included indirectly. +check_unnecessary_include_linux_export_h () { + + git -C "${srctree:-.}" grep --files-with-matches '#include[[:space:]]*' \ + -- '*.[c]' :^tools/ | + xargs -r git -C "${srctree:-.}" grep --files-without-match -E 'EXPORT_SYMBOL((_NS)?(_GPL)?|_GPL_FOR_MODULES)\(.*\)' | + xargs -r printf "%s: warning: EXPORT_SYMBOL() is not used, but #include is present\n" >&2 +} + check_tracked_ignored_files check_missing_include_linux_export_h +check_unnecessary_include_linux_export_h -- cgit v1.2.3-59-g8ed1b From 8c21c4111128365f81a88573eeb2844fa696b299 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 2 Jun 2025 19:55:36 +0900 Subject: module: make __mod_device_table__* symbols static The __mod_device_table__* symbols are only parsed by modpost to generate MODULE_ALIAS() entries from MODULE_DEVICE_TABLE(). Therefore, these symbols do not need to be globally visible, or globally unique. If they are in the global scope, we would worry about the symbol uniqueness, but modpost is fine with parsing multiple symbols with the same name. Signed-off-by: Masahiro Yamada Reviewed-by: Petr Pavlu --- include/linux/module.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/module.h b/include/linux/module.h index 8050f77c3b64..92e1420fccdf 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -249,8 +249,8 @@ struct module_kobject *lookup_or_create_module_kobject(const char *name); #ifdef MODULE /* Creates an alias so file2alias.c can find device table. */ #define MODULE_DEVICE_TABLE(type, name) \ -extern typeof(name) __mod_device_table__##type##__##name \ - __attribute__ ((unused, alias(__stringify(name)))) +static typeof(name) __mod_device_table__##type##__##name \ + __attribute__ ((used, alias(__stringify(name)))) #else /* !MODULE */ #define MODULE_DEVICE_TABLE(type, name) #endif -- cgit v1.2.3-59-g8ed1b From 481d5c31e14347aef33ab715f79831873a60c6e2 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 3 Jun 2025 03:11:41 +0900 Subject: efi/libstub: use 'targets' instead of extra-y in Makefile These objects are built as prerequisites of %.stub.o files. There is no need to use extra-y, which is planned for deprecation. Acked-by: Ard Biesheuvel Signed-off-by: Masahiro Yamada --- drivers/firmware/efi/libstub/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index d23a1b9fed75..5ce9f2098b99 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -102,7 +102,7 @@ lib-$(CONFIG_EFI_ZBOOT) += zboot.o $(zboot-obj-y) lib-$(CONFIG_UNACCEPTED_MEMORY) += unaccepted_memory.o bitmap.o find.o -extra-y := $(lib-y) +targets := $(lib-y) lib-y := $(patsubst %.o,%.stub.o,$(lib-y)) # Even when -mbranch-protection=none is set, Clang will generate a -- cgit v1.2.3-59-g8ed1b From 1bf807b8f7e0eada62007f9f1966f3d22329eef9 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 3 Jun 2025 03:12:53 +0900 Subject: kbuild: set y instead of 1 to KBUILD_{BUILTIN,MODULES} KBUILD_BUILTIN is set to 1 unless you are building only modules. KBUILD_MODULES is set to 1 when you are building only modules (a typical use case is "make modules"). It is more useful to set them to 'y' instead, so we can do something like: always-$(KBUILD_BUILTIN) += vmlinux.lds This works equivalently to: extra-y += vmlinux.lds This allows us to deprecate extra-y. extra-y and always-y are quite similar, and we do not need both. Signed-off-by: Masahiro Yamada Reviewed-by: Nathan Chancellor Reviewed-by: Nicolas Schier --- Documentation/kbuild/makefiles.rst | 4 ++++ Makefile | 16 ++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Documentation/kbuild/makefiles.rst b/Documentation/kbuild/makefiles.rst index 3b9a8bc671e2..18ef339f687b 100644 --- a/Documentation/kbuild/makefiles.rst +++ b/Documentation/kbuild/makefiles.rst @@ -291,6 +291,10 @@ Example:: # arch/x86/kernel/Makefile extra-y += vmlinux.lds +extra-y is now deprecated because this is equivalent to: + + always-$(KBUILD_BUILTIN) += vmlinux.lds + $(extra-y) should only contain targets needed for vmlinux. Kbuild skips extra-y when vmlinux is apparently not a final goal. diff --git a/Makefile b/Makefile index fe2f1cb4d9b7..e07692d485e6 100644 --- a/Makefile +++ b/Makefile @@ -749,7 +749,7 @@ targets := # Normally, just do built-in. KBUILD_MODULES := -KBUILD_BUILTIN := 1 +KBUILD_BUILTIN := y # If we have only "make modules", don't compile built-in objects. ifeq ($(MAKECMDGOALS),modules) @@ -761,11 +761,11 @@ endif # Just "make" or "make all" shall build modules as well ifneq ($(filter all modules nsdeps compile_commands.json clang-%,$(MAKECMDGOALS)),) - KBUILD_MODULES := 1 + KBUILD_MODULES := y endif ifeq ($(MAKECMDGOALS),) - KBUILD_MODULES := 1 + KBUILD_MODULES := y endif export KBUILD_MODULES KBUILD_BUILTIN @@ -1193,7 +1193,7 @@ export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds ifdef CONFIG_TRIM_UNUSED_KSYMS # For the kernel to actually contain only the needed exported symbols, # we have to build modules as well to determine what those symbols are. -KBUILD_MODULES := 1 +KBUILD_MODULES := y endif # '$(AR) mPi' needs 'T' to workaround the bug of llvm-ar <= 14 @@ -1533,7 +1533,7 @@ all: modules # the built-in objects during the descend as well, in order to # make sure the checksums are up to date before we record them. ifdef CONFIG_MODVERSIONS - KBUILD_BUILTIN := 1 + KBUILD_BUILTIN := y endif # Build modules @@ -1542,7 +1542,7 @@ endif # *.ko are usually independent of vmlinux, but CONFIG_DEBUG_INFO_BTF_MODULES # is an exception. ifdef CONFIG_DEBUG_INFO_BTF_MODULES -KBUILD_BUILTIN := 1 +KBUILD_BUILTIN := y modules: vmlinux endif @@ -1858,7 +1858,7 @@ filechk_kernel.release = echo $(KERNELRELEASE) # We are always building only modules. KBUILD_BUILTIN := -KBUILD_MODULES := 1 +KBUILD_MODULES := y build-dir := . @@ -1986,7 +1986,7 @@ endif single-goals := $(addprefix $(build-dir)/, $(single-no-ko)) -KBUILD_MODULES := 1 +KBUILD_MODULES := y endif -- cgit v1.2.3-59-g8ed1b From e21efe833eae4e2a56c2c2a11caae870a65926fa Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 3 Jun 2025 03:12:54 +0900 Subject: arch: use always-$(KBUILD_BUILTIN) for vmlinux.lds The extra-y syntax is deprecated. Instead, use always-$(KBUILD_BUILTIN), which behaves equivalently. Signed-off-by: Masahiro Yamada Acked-by: Johannes Berg Reviewed-by: Nicolas Schier --- arch/alpha/kernel/Makefile | 2 +- arch/arc/kernel/Makefile | 2 +- arch/arm/kernel/Makefile | 2 +- arch/arm64/kernel/Makefile | 2 +- arch/csky/kernel/Makefile | 2 +- arch/hexagon/kernel/Makefile | 2 +- arch/loongarch/kernel/Makefile | 2 +- arch/m68k/kernel/Makefile | 2 +- arch/microblaze/kernel/Makefile | 2 +- arch/mips/kernel/Makefile | 2 +- arch/nios2/kernel/Makefile | 2 +- arch/openrisc/kernel/Makefile | 2 +- arch/parisc/kernel/Makefile | 2 +- arch/powerpc/kernel/Makefile | 2 +- arch/riscv/kernel/Makefile | 2 +- arch/s390/kernel/Makefile | 2 +- arch/sh/kernel/Makefile | 2 +- arch/sparc/kernel/Makefile | 2 +- arch/um/kernel/Makefile | 2 +- arch/x86/kernel/Makefile | 2 +- arch/xtensa/kernel/Makefile | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile index b6c862dff1f6..187cd8df2faf 100644 --- a/arch/alpha/kernel/Makefile +++ b/arch/alpha/kernel/Makefile @@ -3,7 +3,7 @@ # Makefile for the linux kernel. # -extra-y := vmlinux.lds +always-$(KBUILD_BUILTIN) := vmlinux.lds asflags-y := $(KBUILD_CFLAGS) ccflags-y := -Wno-sign-compare diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile index 95fbf9364c67..fa94fff02419 100644 --- a/arch/arc/kernel/Makefile +++ b/arch/arc/kernel/Makefile @@ -26,4 +26,4 @@ ifdef CONFIG_ISA_ARCOMPACT CFLAGS_fpu.o += -mdpfp endif -extra-y := vmlinux.lds +always-$(KBUILD_BUILTIN) := vmlinux.lds diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index b3333d070390..afc9de7ef9a1 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -104,4 +104,4 @@ obj-$(CONFIG_HAVE_ARM_SMCCC) += smccc-call.o obj-$(CONFIG_GENERIC_CPU_VULNERABILITIES) += spectre.o -extra-y := vmlinux.lds +always-$(KBUILD_BUILTIN) := vmlinux.lds diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 71c29a2a2f19..2920b0a51403 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -78,7 +78,7 @@ $(obj)/vdso32-wrap.o: $(obj)/vdso32/vdso.so obj-y += probes/ obj-y += head.o -extra-y += vmlinux.lds +always-$(KBUILD_BUILTIN) += vmlinux.lds ifeq ($(CONFIG_DEBUG_EFI),y) AFLAGS_head.o += -DVMLINUX_PATH="\"$(realpath $(objtree)/vmlinux)\"" diff --git a/arch/csky/kernel/Makefile b/arch/csky/kernel/Makefile index de1c3472e8f0..a406a4ac2378 100644 --- a/arch/csky/kernel/Makefile +++ b/arch/csky/kernel/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -extra-y := vmlinux.lds +always-$(KBUILD_BUILTIN) := vmlinux.lds obj-y += head.o entry.o atomic.o signal.o traps.o irq.o time.o vdso.o vdso/ obj-y += power.o syscall.o syscall_table.o setup.o diff --git a/arch/hexagon/kernel/Makefile b/arch/hexagon/kernel/Makefile index 3fdf937eb572..8e0fb4a62315 100644 --- a/arch/hexagon/kernel/Makefile +++ b/arch/hexagon/kernel/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -extra-y := vmlinux.lds +always-$(KBUILD_BUILTIN) := vmlinux.lds obj-y += head.o obj-$(CONFIG_SMP) += smp.o diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile index f9dcaa60033d..6f5a4574a911 100644 --- a/arch/loongarch/kernel/Makefile +++ b/arch/loongarch/kernel/Makefile @@ -5,7 +5,7 @@ OBJECT_FILES_NON_STANDARD_head.o := y -extra-y := vmlinux.lds +always-$(KBUILD_BUILTIN) := vmlinux.lds obj-y += head.o cpu-probe.o cacheinfo.o env.o setup.o entry.o genex.o \ traps.o irq.o idle.o process.o dma.o mem.o reset.o switch.o \ diff --git a/arch/m68k/kernel/Makefile b/arch/m68k/kernel/Makefile index 6c732ed3998b..57c1b3e8d60e 100644 --- a/arch/m68k/kernel/Makefile +++ b/arch/m68k/kernel/Makefile @@ -3,7 +3,7 @@ # Makefile for the linux kernel. # -extra-y += vmlinux.lds +always-$(KBUILD_BUILTIN) += vmlinux.lds obj-$(CONFIG_MMU_MOTOROLA) := head.o obj-$(CONFIG_SUN3) := sun3-head.o diff --git a/arch/microblaze/kernel/Makefile b/arch/microblaze/kernel/Makefile index 85c4d29ef43e..241e466e7333 100644 --- a/arch/microblaze/kernel/Makefile +++ b/arch/microblaze/kernel/Makefile @@ -11,7 +11,7 @@ CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_process.o = -pg endif -extra-y := vmlinux.lds +always-$(KBUILD_BUILTIN) := vmlinux.lds obj-y += head.o dma.o exceptions.o \ hw_exception_handler.o irq.o \ diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index ecf3278a32f7..95a1e674fd67 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -3,7 +3,7 @@ # Makefile for the Linux/MIPS kernel. # -extra-y := vmlinux.lds +always-$(KBUILD_BUILTIN) := vmlinux.lds obj-y += head.o branch.o cmpxchg.o elf.o entry.o genex.o idle.o irq.o \ process.o prom.o ptrace.o reset.o setup.o signal.o \ diff --git a/arch/nios2/kernel/Makefile b/arch/nios2/kernel/Makefile index 78a913181fa1..4dce965a7b73 100644 --- a/arch/nios2/kernel/Makefile +++ b/arch/nios2/kernel/Makefile @@ -3,7 +3,7 @@ # Makefile for the nios2 linux kernel. # -extra-y += vmlinux.lds +always-$(KBUILD_BUILTIN) += vmlinux.lds obj-y += head.o obj-y += cpuinfo.o diff --git a/arch/openrisc/kernel/Makefile b/arch/openrisc/kernel/Makefile index e4c7d9bdd598..58e6a1b525b7 100644 --- a/arch/openrisc/kernel/Makefile +++ b/arch/openrisc/kernel/Makefile @@ -3,7 +3,7 @@ # Makefile for the linux kernel. # -extra-y := vmlinux.lds +always-$(KBUILD_BUILTIN) := vmlinux.lds obj-y := head.o setup.o or32_ksyms.o process.o dma.o \ traps.o time.o irq.o entry.o ptrace.o signal.o \ diff --git a/arch/parisc/kernel/Makefile b/arch/parisc/kernel/Makefile index 5ab0467be70a..d5055ba33722 100644 --- a/arch/parisc/kernel/Makefile +++ b/arch/parisc/kernel/Makefile @@ -3,7 +3,7 @@ # Makefile for arch/parisc/kernel # -extra-y := vmlinux.lds +always-$(KBUILD_BUILTIN) := vmlinux.lds obj-y := head.o cache.o pacache.o setup.o pdt.o traps.o time.o irq.o \ syscall.o entry.o sys_parisc.o firmware.o \ diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 6ac621155ec3..4d2daa8e7bca 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -126,7 +126,7 @@ obj-$(CONFIG_PPC_BOOK3S_32) += head_book3s_32.o obj-$(CONFIG_44x) += head_44x.o obj-$(CONFIG_PPC_8xx) += head_8xx.o obj-$(CONFIG_PPC_85xx) += head_85xx.o -extra-y += vmlinux.lds +always-$(KBUILD_BUILTIN) += vmlinux.lds obj-$(CONFIG_RELOCATABLE) += reloc_$(BITS).o diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index f7480c9c6f8d..48dcaf2efae8 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -43,7 +43,7 @@ CFLAGS_sbi_ecall.o += -D__NO_FORTIFY endif endif -extra-y += vmlinux.lds +always-$(KBUILD_BUILTIN) += vmlinux.lds obj-y += head.o obj-y += soc.o diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index db5f3a3faefb..ea5ed6654050 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -46,7 +46,7 @@ obj-y += nospec-branch.o ipl_vmparm.o machine_kexec_reloc.o unwind_bc.o obj-y += smp.o text_amode31.o stacktrace.o abs_lowcore.o facility.o uv.o wti.o obj-y += diag/ -extra-y += vmlinux.lds +always-$(KBUILD_BUILTIN) += vmlinux.lds obj-$(CONFIG_SYSFS) += nospec-sysfs.o CFLAGS_REMOVE_nospec-branch.o += $(CC_FLAGS_EXPOLINE) diff --git a/arch/sh/kernel/Makefile b/arch/sh/kernel/Makefile index 7b453592adaf..5ef123bc63f8 100644 --- a/arch/sh/kernel/Makefile +++ b/arch/sh/kernel/Makefile @@ -3,7 +3,7 @@ # Makefile for the Linux/SuperH kernel. # -extra-y := vmlinux.lds +always-$(KBUILD_BUILTIN) := vmlinux.lds ifdef CONFIG_FUNCTION_TRACER # Do not profile debug and lowlevel utilities diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index 58ea4ef9b622..2859842d6bb7 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -9,7 +9,7 @@ asflags-y := -ansi # Undefine sparc when processing vmlinux.lds - it is used # And teach CPP we are doing $(BITS) builds (for this case) CPPFLAGS_vmlinux.lds := -Usparc -m$(BITS) -extra-y += vmlinux.lds +always-$(KBUILD_BUILTIN) += vmlinux.lds ifdef CONFIG_FUNCTION_TRACER # Do not profile debug and lowlevel utilities diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile index 4df1cd0d2017..821bf4027a23 100644 --- a/arch/um/kernel/Makefile +++ b/arch/um/kernel/Makefile @@ -12,7 +12,7 @@ CPPFLAGS_vmlinux.lds := -DSTART=$(LDS_START) \ -DELF_ARCH=$(LDS_ELF_ARCH) \ -DELF_FORMAT=$(LDS_ELF_FORMAT) \ $(LDS_EXTRA) -extra-y := vmlinux.lds +always-$(KBUILD_BUILTIN) := vmlinux.lds obj-y = config.o exec.o exitcode.o irq.o ksyms.o mem.o \ physmem.o process.o ptrace.o reboot.o sigio.o \ diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 84cfa179802c..9a30c9816b16 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -3,7 +3,7 @@ # Makefile for the linux kernel. # -extra-y += vmlinux.lds +always-$(KBUILD_BUILTIN) += vmlinux.lds CPPFLAGS_vmlinux.lds += -U$(UTS_MACHINE) diff --git a/arch/xtensa/kernel/Makefile b/arch/xtensa/kernel/Makefile index f28b8e3d717e..d3ef0407401f 100644 --- a/arch/xtensa/kernel/Makefile +++ b/arch/xtensa/kernel/Makefile @@ -3,7 +3,7 @@ # Makefile for the Linux/Xtensa kernel. # -extra-y := vmlinux.lds +always-$(KBUILD_BUILTIN) := vmlinux.lds obj-y := head.o align.o coprocessor.o entry.o irq.o platform.o process.o \ ptrace.o setup.o signal.o stacktrace.o syscall.o time.o traps.o \ -- cgit v1.2.3-59-g8ed1b From c50a04f8f45c7f13972f9097622d1d929033ea8c Mon Sep 17 00:00:00 2001 From: Petr Pavlu Date: Tue, 3 Jun 2025 15:02:09 +0200 Subject: genksyms: Fix enum consts from a reference affecting new values Enumeration constants read from a symbol reference file can incorrectly affect new enumeration constants parsed from an actual input file. Example: $ cat test.c enum { E_A, E_B, E_MAX }; struct bar { int mem[E_MAX]; }; int foo(struct bar *a) {} __GENKSYMS_EXPORT_SYMBOL(foo); $ cat test.c | ./scripts/genksyms/genksyms -T test.0.symtypes #SYMVER foo 0x070d854d $ cat test.0.symtypes E#E_MAX 2 s#bar struct bar { int mem [ E#E_MAX ] ; } foo int foo ( s#bar * ) $ cat test.c | ./scripts/genksyms/genksyms -T test.1.symtypes -r test.0.symtypes :4: warning: foo: modversion changed because of changes in enum constant E_MAX #SYMVER foo 0x9c9dfd81 $ cat test.1.symtypes E#E_MAX ( 2 ) + 3 s#bar struct bar { int mem [ E#E_MAX ] ; } foo int foo ( s#bar * ) The __add_symbol() function includes logic to handle the incrementation of enumeration values, but this code is also invoked when reading a reference file. As a result, the variables last_enum_expr and enum_counter might be incorrectly set after reading the reference file, which later affects parsing of the actual input. Fix the problem by splitting the logic for the incrementation of enumeration values into a separate function process_enum() and call it from __add_symbol() only when processing non-reference data. Fixes: e37ddb825003 ("genksyms: Track changes to enum constants") Signed-off-by: Petr Pavlu Signed-off-by: Masahiro Yamada --- scripts/genksyms/genksyms.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/scripts/genksyms/genksyms.c b/scripts/genksyms/genksyms.c index 8b0d7ac73dbb..83e48670c2fc 100644 --- a/scripts/genksyms/genksyms.c +++ b/scripts/genksyms/genksyms.c @@ -181,13 +181,9 @@ static int is_unknown_symbol(struct symbol *sym) strcmp(defn->string, "{") == 0); } -static struct symbol *__add_symbol(const char *name, enum symbol_type type, - struct string_list *defn, int is_extern, - int is_reference) +static struct string_list *process_enum(const char *name, enum symbol_type type, + struct string_list *defn) { - unsigned long h; - struct symbol *sym; - enum symbol_status status = STATUS_UNCHANGED; /* The parser adds symbols in the order their declaration completes, * so it is safe to store the value of the previous enum constant in * a static variable. @@ -216,7 +212,7 @@ static struct symbol *__add_symbol(const char *name, enum symbol_type type, defn = mk_node(buf); } } - } else if (type == SYM_ENUM) { + } else { free_list(last_enum_expr, NULL); last_enum_expr = NULL; enum_counter = 0; @@ -225,6 +221,23 @@ static struct symbol *__add_symbol(const char *name, enum symbol_type type, return NULL; } + return defn; +} + +static struct symbol *__add_symbol(const char *name, enum symbol_type type, + struct string_list *defn, int is_extern, + int is_reference) +{ + unsigned long h; + struct symbol *sym; + enum symbol_status status = STATUS_UNCHANGED; + + if ((type == SYM_ENUM_CONST || type == SYM_ENUM) && !is_reference) { + defn = process_enum(name, type, defn); + if (defn == NULL) + return NULL; + } + h = crc32(name); hash_for_each_possible(symbol_hashtable, sym, hnode, h) { if (map_to_ns(sym->type) != map_to_ns(type) || -- cgit v1.2.3-59-g8ed1b