scripts: fix modpost

This commit is contained in:
amazingfate
2023-05-16 17:48:03 +08:00
parent ee1dbc14da
commit 95fa2468ef
3 changed files with 60 additions and 88 deletions

View File

@@ -17,6 +17,7 @@
#include <ctype.h>
#include <string.h>
#include <limits.h>
#include <stdbool.h>
#include <errno.h>
#include "modpost.h"
#include "../../include/linux/license.h"
@@ -29,20 +30,16 @@ static int have_vmlinux = 0;
static int all_versions = 0;
/* If we are modposting external module set to 1 */
static int external_module = 0;
#define MODULE_SCMVERSION_SIZE 64
static char module_scmversion[MODULE_SCMVERSION_SIZE];
/* Only warn about unresolved symbols */
static int warn_unresolved = 0;
/* How a symbol is exported */
static int sec_mismatch_count = 0;
static int sec_mismatch_warn_only = true;
static int sec_mismatch_fatal = 0;
/* ignore missing files */
static int ignore_missing_files;
/* If set to 1, only warn (instead of error) about missing ns imports */
static int allow_missing_ns_imports;
static bool error_occurred;
enum export {
export_plain, export_unused, export_gpl,
export_unused_gpl, export_gpl_future, export_unknown
@@ -81,8 +78,14 @@ modpost_log(enum loglevel loglevel, const char *fmt, ...)
if (loglevel == LOG_FATAL)
exit(1);
if (loglevel == LOG_ERROR)
error_occurred = true;
}
static inline bool strends(const char *str, const char *postfix)
{
if (strlen(str) < strlen(postfix))
return false;
return strcmp(str + strlen(str) - strlen(postfix), postfix) == 0;
}
void *do_nofail(void *ptr, const char *expr)
@@ -400,8 +403,8 @@ static void sym_update_namespace(const char *symname, const char *namespace)
* actually an assertion.
*/
if (!s) {
error("Could not update namespace(%s) for symbol %s\n",
namespace, symname);
merror("Could not update namespace(%s) for symbol %s\n",
namespace, symname);
return;
}
@@ -423,9 +426,10 @@ static struct symbol *sym_add_exported(const char *name, struct module *mod,
s = new_symbol(name, mod, export);
} else if (!external_module || s->module->is_vmlinux ||
s->module == mod) {
fatal("%s: '%s' exported twice. Previous export was in %s%s\n",
mod->name, name, s->module->name,
s->module->is_vmlinux ? "" : ".ko");
warn("%s: '%s' exported twice. Previous export was in %s%s\n",
mod->name, name, s->module->name,
s->module->is_vmlinux ? "" : ".ko");
return s;
}
s->module = mod;
@@ -1981,10 +1985,6 @@ static char *remove_dot(char *s)
size_t m = strspn(s + n + 1, "0123456789");
if (m && (s[n + m + 1] == '.' || s[n + m + 1] == 0))
s[n] = 0;
/* strip trailing .lto */
if (strends(s, ".lto"))
s[strlen(s) - 4] = '\0';
}
return s;
}
@@ -2008,9 +2008,6 @@ static void read_symbols(const char *modname)
/* strip trailing .o */
tmp = NOFAIL(strdup(modname));
tmp[strlen(tmp) - 2] = '\0';
/* strip trailing .lto */
if (strends(tmp, ".lto"))
tmp[strlen(tmp) - 4] = '\0';
mod = new_module(tmp);
free(tmp);
}
@@ -2018,7 +2015,7 @@ static void read_symbols(const char *modname)
if (!mod->is_vmlinux) {
license = get_modinfo(&info, "license");
if (!license)
error("missing MODULE_LICENSE() in %s\n", modname);
warn("missing MODULE_LICENSE() in %s\n", modname);
while (license) {
if (license_is_gpl_compatible(license))
mod->gpl_compatible = 1;
@@ -2145,11 +2142,11 @@ static void check_for_gpl_usage(enum export exp, const char *m, const char *s)
{
switch (exp) {
case export_gpl:
error("GPL-incompatible module %s.ko uses GPL-only symbol '%s'\n",
fatal("GPL-incompatible module %s.ko uses GPL-only symbol '%s'\n",
m, s);
break;
case export_unused_gpl:
error("GPL-incompatible module %s.ko uses GPL-only symbol marked UNUSED '%s'\n",
fatal("GPL-incompatible module %s.ko uses GPL-only symbol marked UNUSED '%s'\n",
m, s);
break;
case export_gpl_future:
@@ -2178,18 +2175,22 @@ static void check_for_unused(enum export exp, const char *m, const char *s)
}
}
static void check_exports(struct module *mod)
static int check_exports(struct module *mod)
{
struct symbol *s, *exp;
int err = 0;
for (s = mod->unres; s; s = s->next) {
const char *basename;
exp = find_symbol(s->name);
if (!exp || exp->module == mod) {
if (have_vmlinux && !s->weak)
if (have_vmlinux && !s->weak) {
modpost_log(warn_unresolved ? LOG_WARN : LOG_ERROR,
"\"%s\" [%s.ko] undefined!\n",
s->name, mod->name);
if (!warn_unresolved)
err = 1;
}
continue;
}
basename = strrchr(mod->name, '/');
@@ -2203,6 +2204,8 @@ static void check_exports(struct module *mod)
modpost_log(allow_missing_ns_imports ? LOG_WARN : LOG_ERROR,
"module %s uses symbol %s from namespace %s, but does not import it.\n",
basename, exp->name, exp->namespace);
if (!allow_missing_ns_imports)
err = 1;
add_namespace(&mod->missing_namespaces, exp->namespace);
}
@@ -2210,9 +2213,11 @@ static void check_exports(struct module *mod)
check_for_gpl_usage(exp->export, basename, exp->name);
check_for_unused(exp->export, basename, exp->name);
}
return err;
}
static void check_modname_len(struct module *mod)
static int check_modname_len(struct module *mod)
{
const char *mod_name;
@@ -2221,8 +2226,12 @@ static void check_modname_len(struct module *mod)
mod_name = mod->name;
else
mod_name++;
if (strlen(mod_name) >= MODULE_NAME_LEN)
error("module name is too long [%s.ko]\n", mod->name);
if (strlen(mod_name) >= MODULE_NAME_LEN) {
merror("module name is too long [%s.ko]\n", mod->name);
return 1;
}
return 0;
}
/**
@@ -2264,20 +2273,6 @@ static void add_intree_flag(struct buffer *b, int is_intree)
buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n");
}
/**
* add_scmversion() - Adds the MODULE_INFO macro for the scmversion.
* @b: Buffer to append to.
*
* This function fills in the module attribute `scmversion` for the kernel
* module. This is useful for determining a given module's SCM version on
* device via /sys/modules/<module>/scmversion and/or using the modinfo tool.
*/
static void add_scmversion(struct buffer *b)
{
if (module_scmversion[0] != '\0')
buf_printf(b, "\nMODULE_INFO(scmversion, \"%s\");\n", module_scmversion);
}
/* Cannot check for assembler */
static void add_retpoline(struct buffer *b)
{
@@ -2295,9 +2290,10 @@ static void add_staging_flag(struct buffer *b, const char *name)
/**
* Record CRCs for unresolved symbols
**/
static void add_versions(struct buffer *b, struct module *mod)
static int add_versions(struct buffer *b, struct module *mod)
{
struct symbol *s, *exp;
int err = 0;
for (s = mod->unres; s; s = s->next) {
exp = find_symbol(s->name);
@@ -2309,7 +2305,7 @@ static void add_versions(struct buffer *b, struct module *mod)
}
if (!modversions)
return;
return err;
buf_printf(b, "\n");
buf_printf(b, "static const struct modversion_info ____versions[]\n");
@@ -2324,8 +2320,9 @@ static void add_versions(struct buffer *b, struct module *mod)
continue;
}
if (strlen(s->name) >= MODULE_NAME_LEN) {
error("too long symbol \"%s\" [%s.ko]\n",
s->name, mod->name);
merror("too long symbol \"%s\" [%s.ko]\n",
s->name, mod->name);
err = 1;
break;
}
buf_printf(b, "\t{ %#8x, \"%s\" },\n",
@@ -2333,6 +2330,8 @@ static void add_versions(struct buffer *b, struct module *mod)
}
buf_printf(b, "};\n");
return err;
}
static void add_depends(struct buffer *b, struct module *mod)
@@ -2543,11 +2542,12 @@ int main(int argc, char **argv)
char *missing_namespace_deps = NULL;
char *dump_write = NULL, *files_source = NULL;
int opt;
int err;
int n;
struct dump_list *dump_read_start = NULL;
struct dump_list **dump_read_iter = &dump_read_start;
while ((opt = getopt(argc, argv, "ei:mnT:o:awENd:v:")) != -1) {
while ((opt = getopt(argc, argv, "ei:mnT:o:awENd:")) != -1) {
switch (opt) {
case 'e':
external_module = 1;
@@ -2577,7 +2577,7 @@ int main(int argc, char **argv)
warn_unresolved = 1;
break;
case 'E':
sec_mismatch_warn_only = false;
sec_mismatch_fatal = 1;
break;
case 'N':
allow_missing_ns_imports = 1;
@@ -2585,9 +2585,6 @@ int main(int argc, char **argv)
case 'd':
missing_namespace_deps = optarg;
break;
case 'v':
strncpy(module_scmversion, optarg, sizeof(module_scmversion) - 1);
break;
default:
exit(1);
}
@@ -2615,6 +2612,8 @@ int main(int argc, char **argv)
if (!have_vmlinux)
warn("Symbol info of vmlinux is missing. Unresolved symbol check will be entirely skipped.\n");
err = 0;
for (mod = modules; mod; mod = mod->next) {
char fname[PATH_MAX];
@@ -2623,18 +2622,17 @@ int main(int argc, char **argv)
buf.pos = 0;
check_modname_len(mod);
check_exports(mod);
err |= check_modname_len(mod);
err |= check_exports(mod);
add_header(&buf, mod);
add_intree_flag(&buf, !external_module);
add_retpoline(&buf);
add_staging_flag(&buf, mod->name);
add_versions(&buf, mod);
err |= add_versions(&buf, mod);
add_depends(&buf, mod);
add_moddevtable(&buf, mod);
add_srcversion(&buf, mod);
add_scmversion(&buf);
sprintf(fname, "%s.mod.c", mod->name);
write_if_changed(&buf, fname);
@@ -2645,21 +2643,21 @@ int main(int argc, char **argv)
if (dump_write)
write_dump(dump_write);
if (sec_mismatch_count && !sec_mismatch_warn_only)
error("Section mismatches detected.\n"
if (sec_mismatch_count && sec_mismatch_fatal)
fatal("Section mismatches detected.\n"
"Set CONFIG_SECTION_MISMATCH_WARN_ONLY=y to allow them.\n");
for (n = 0; n < SYMBOL_HASH_SIZE; n++) {
struct symbol *s;
for (s = symbolhash[n]; s; s = s->next) {
if (s->is_static)
error("\"%s\" [%s] is a static %s\n",
s->name, s->module->name,
export_str(s->export));
warn("\"%s\" [%s] is a static %s\n",
s->name, s->module->name,
export_str(s->export));
}
}
free(buf.p);
return error_occurred ? 1 : 0;
return err;
}

View File

@@ -2,7 +2,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -181,14 +180,6 @@ static inline unsigned int get_secindex(const struct elf_info *info,
return info->symtab_shndx_start[sym - info->symtab_start];
}
static inline bool strends(const char *str, const char *postfix)
{
if (strlen(str) < strlen(postfix))
return false;
return strcmp(str + strlen(str) - strlen(postfix), postfix) == 0;
}
/* file2alias.c */
extern unsigned int cross_build;
void handle_moddevtable(struct module *mod, struct elf_info *info,
@@ -210,19 +201,6 @@ enum loglevel {
void modpost_log(enum loglevel loglevel, const char *fmt, ...);
/*
* warn - show the given message, then let modpost continue running, still
* allowing modpost to exit successfully. This should be used when
* we still allow to generate vmlinux and modules.
*
* error - show the given message, then let modpost continue running, but fail
* in the end. This should be used when we should stop building vmlinux
* or modules, but we can continue running modpost to catch as many
* issues as possible.
*
* fatal - show the given message, and bail out immediately. This should be
* used when there is no point to continue running modpost.
*/
#define warn(fmt, args...) modpost_log(LOG_WARN, fmt, ##args)
#define error(fmt, args...) modpost_log(LOG_ERROR, fmt, ##args)
#define merror(fmt, args...) modpost_log(LOG_ERROR, fmt, ##args)
#define fatal(fmt, args...) modpost_log(LOG_FATAL, fmt, ##args)

View File

@@ -391,14 +391,10 @@ void get_src_version(const char *modname, char sum[], unsigned sumlen)
struct md4_ctx md;
char *fname;
char filelist[PATH_MAX + 1];
int postfix_len = 1;
if (strends(modname, ".lto.o"))
postfix_len = 5;
/* objects for a module are listed in the first line of *.mod file. */
snprintf(filelist, sizeof(filelist), "%.*smod",
(int)strlen(modname) - postfix_len, modname);
(int)strlen(modname) - 1, modname);
buf = read_text_file(filelist);